Ввод/вывод в Trio
Абстрактный API потоков
Trio предоставляет набор абстрактных базовых классов, которые определяют стандартный интерфейс для однонаправленных и двунаправленных байтовых потоков.
Почему это полезно? Потому что это позволяет вам писать обобщенные реализации протоколов, которые могут работать с произвольными транспортными средствами, и легко создавать сложные конфигурации транспорта. Вот несколько примеров:
trio.SocketStreamоборачивает необработанный сокет (например, TCP-соединение по сети) и преобразует его в стандартный интерфейс потока.trio.SSLStream— это «адаптер потока», который может принять любой объект, реализующий интерфейсtrio.abc.Stream, и преобразовать его в зашифрованный поток. В Trio стандартный способ общения по SSL через сеть — это обернутьSSLStreamвокругSocketStream.-
Если вы запускаете подпроцесс, вы можете получить
SendStream, который позволяет записывать в его стандартный ввод, иReceiveStream, который позволяет читать из его стандартного вывода. Если по какой-то причине вам нужно было использовать SSL для взаимодействия с подпроцессом, вы могли бы использоватьStapledStream, чтобы объединить его стандартный ввод/вывод в один двунаправленныйStream, а затем обернуть это вSSLStream:ssl_context = ssl.create_default_context() ssl_context.check_hostname = False s = SSLStream(StapledStream(process.stdin, process.stdout), ssl_context)
-
Иногда вам нужно подключиться к серверу HTTPS, но вам нужно пройти через веб-прокси… и прокси также использует HTTPS. Поэтому вам приходится делать SSL поверх SSL. В Trio это тривиально — просто оберните ваш первый
SSLStreamво второйSSLStream:# Get a raw SocketStream connection to the proxy: s0 = await open_tcp_stream("proxy", 443) # Set up SSL connection to proxy: s1 = SSLStream(s0, proxy_ssl_context, server_hostname="proxy") # Request a connection to the website await s1.send_all(b"CONNECT website:443 / HTTP/1.0\r\n\r\n") await check_CONNECT_response(s1) # Set up SSL connection to the real website. Notice that s1 is # already an SSLStream object, and here we're wrapping a second # SSLStream object around it. s2 = SSLStream(s1, website_ssl_context, server_hostname="website") # Make our request await s2.send_all(b"GET /index.html HTTP/1.0\r\n\r\n") ... Модуль
trio.testingпредоставляет набор гибких реализаций объектов потоков в памяти, поэтому если у вас есть реализация протокола для тестирования, вы можете запустить две задачи, настроить виртуальный «сокет», соединяющий их, и затем делать такие вещи, как вводить случайные, но повторяющиеся задержки в соединение.
Абстрактные базовые классы
Абстрактный базовый класс | Наследуется от… | Добавляет эти абстрактные методы… | И эти конкретные методы. | Примеры реализаций |
|---|---|---|---|---|
| ||||
| ||||
| ||||
-
Стандартный интерфейс для ресурсов, которые необходимо очистить, и где эта очистка может потребовать блокирующих операций.
Этот класс различает «вежливые» закрытия, которые могут выполнять ввод/вывод и, следовательно, блокироваться, и «принудительное» закрытие, которое не может. Например, чистая остановка TLS-шифрованного соединения требует отправки сообщения «до свидания»; но если узел стал нереагирующим, то отправка этого сообщения может блокироваться вечно, поэтому мы можем просто прервать соединение. Поэтому метод
aclose()необычен тем, что он всегда должен закрывать соединение (или, по крайней мере, сделать все возможное) даже если произойдет ошибка; ошибка указывает на неудачу достижения вежливости, а не на неудачу закрытия соединения.Объекты, реализующие этот интерфейс, могут использоваться в качестве асинхронных контекстных менеджеров, т.е. вы можете написать:
async with create_resource() as some_async_resource: ...Вход в контекстный менеджер является синхронным (не контрольная точка); при выходе из него вызывается метод
aclose(). Стандартные реализации__aenter__и__aexit__должны быть достаточными для всех подклассов.-
Закрыть этот ресурс, возможно, заблокировав.
ВАЖНО: Этот метод может заблокироваться, чтобы выполнить «вежливое» завершение. Но если это не удастся, то он все равно обязан закрыть все базовые ресурсы перед возвратом. Ошибка из этого метода указывает на неудачу достижения вежливости, а не на неудачу закрытия соединения.
Например, предположим, что мы вызываем
aclose()для TLS-шифрованного соединения. Это требует отправки сообщения «до свидания»; но если узел стал нереагирующим, то наша попытка отправить это сообщение может заблокироваться навсегда, и в конечном итоге истечь и быть отменена. В этом случае методaclose()вSSLStreamнемедленно закроет базовый поток транспорта с помощьюtrio.aclose_forcefully()перед поднятиемCancelled.Если ресурс уже закрыт, то этот метод должен успешно завершиться без ошибок.
После завершения этого метода любые другие ожидающие или будущие операции с этим ресурсом, как правило, должны поднимать
ClosedResourceError, если нет веской причины поступить иначе.См. также:
trio.aclose_forcefully().
abstractmethod await aclose() → None -
class trio.abc.AsyncResource
-
Закрыть асинхронный ресурс или асинхронный генератор немедленно, без блокировки для выполнения плавного завершения.
AsyncResourceобъекты гарантируют, что если их методaclose()прерван, то они всё равно закроют ресурс (хотя и, возможно, не плавно).aclose_forcefully()— это удобная функция, которая использует это поведение, чтобы позволить вам принудительно закрыть ресурс без блокировки: она работает, вызываяawait resource.aclose()и затем немедленно прерывая его.Большинству пользователей это не потребуется, но это может быть полезно на путях очистки, где вы не можете позволить себе блокировку, или если вы хотите закрыть ресурс и не беспокоитесь о плавном завершении. Например, если
SSLStreamстолкнётся с ошибкой и не сможет выполнить своё плавное завершение, то нет смысла ждать плавного закрытия базового транспорта, поэтому он вызываетawait aclose_forcefully(self.transport_stream).Обратите внимание, что эта функция асинхронная и работает как контрольная точка, но, в отличие от большинства асинхронных функций, она не может блокироваться неопределённо долго (по крайней мере, предполагая, что базовый объект ресурса реализован правильно).
await trio.aclose_forcefully(resource: AsyncResource) → None
-
Bases:
AsyncResourceСтандартный интерфейс для отправки данных по потоку байтов.
Базовый поток может быть однонаправленным или двунаправленным. Если он двунаправленный, то, вероятно, вы также хотите реализовать
ReceiveStream, что делает ваш объектStream.SendStreamобъекты также реализуют интерфейсAsyncResource, поэтому их можно закрыть, вызвавaclose()или используя блокasync with.Если вы хотите отправлять объекты Python, а не сырые байты, см.
SendChannel.-
Отправляет заданные данные через поток, блокируясь при необходимости.
-
data (bytes, bytearray, или memoryview) – Данные для отправки.
-
trio.BusyResourceError – если другая задача уже выполняет
send_all(),wait_send_all_might_not_block()илиHalfCloseableStream.send_eof()на этом потоке.trio.BrokenResourceError – если что-то пошло не так, и поток сломан.
trio.ClosedResourceError – если вы ранее закрыли этот объект потока, или если другая задача закрывает этот объект потока во время выполнения
send_all().
Параметры:
Исключения:
Большинство операций низкого уровня в Trio обеспечивают гарантию: если они возбуждают
trio.Cancelled, это означает, что они не произвели никакого эффекта, поэтому система остаётся в известном состоянии. Это неверно дляsend_all(). Если эта операция вызываетtrio.Cancelled(или любую другую ошибку), то она может отправить некоторые, все или ни одного из запрошенных данных, и нет способа узнать, какие именно. -
abstractmethod await send_all(data: bytes | bytearray | memoryview) → None-
Блокировать, пока не станет возможным, что
send_all()может не заблокироваться.Этот метод может вернуть раньше: возможно, что после возврата
send_all()всё ещё будет блокироваться. (В худшем случае, если нет лучшей реализации, то он может всегда возвращаться немедленно без блокировки. Однако приятно делать лучше, когда это возможно.)Этот метод не должен возвращаться поздно: если возможно, что
send_all()завершится без блокировки, то он должен вернуть значение. При реализации следует ориентироваться на раннее возвращение.-
trio.BusyResourceError – если другая задача уже выполняет
send_all(),wait_send_all_might_not_block()илиHalfCloseableStream.send_eof()на этом потоке.trio.BrokenResourceError – если что-то пошло не так, и поток сломан.
trio.ClosedResourceError – если вы ранее закрыли этот объект потока, или если другая задача закрывает этот объект потока во время выполнения
wait_send_all_might_not_block().
Исключения:
Примечание
Этот метод предназначен для помощи в реализации протоколов, которые хотят отложить выбор отправляемых данных до последнего момента. Например, предположим, что вы работаете над реализацией сервера удалённой графики, такого как VNC, и соединение по сети в настоящее время перегружено, так что если вы вызовете
send_all()сейчас, то он будет ожидать 0,5 секунды, прежде чем фактически что-то отправлять. В этом случае нет смысла делать снимок экрана, затем ждать 0,5 секунды, а затем отправлять его, потому что экран будет меняться, пока вы ждёте; лучше подождать 0,5 секунды, затем сделать снимок экрана и отправить его, так как таким образом отправляемые данные будут более актуальными. Использованиеwait_send_all_might_not_block()позволяет реализовать лучшую стратегию.Если вы используете этот метод, возможно, вам также стоит почитать
TCP_NOTSENT_LOWAT.Дополнительное чтение:
Приоритетизация работает только тогда, когда есть ожидающие данные для приоритезации
WWDC 2015: Ваше приложение и сети следующего поколения: слайды, видео и транскрипт
-
abstractmethod await wait_send_all_might_not_block() → None -
class trio.abc.SendStream
-
Bases:
AsyncResourceСтандартный интерфейс для получения данных из потока байтов.
Основной поток может быть однонаправленным или двунаправленным. Если он двунаправленный, вероятно, вам также потребуется реализовать
SendStream, что сделает ваш объектStream.ReceiveStreamобъекты также реализуют интерфейсAsyncResource, поэтому их можно закрыть, вызвавaclose()или используя блокasync with.Если вы хотите получать объекты Python, а не сырые байты, см.
ReceiveChannel.ReceiveStreamобъекты могут использоваться в циклахasync for. Каждый итерация вернёт кусок байтов произвольного размера, как если бы вы вызвалиreceive_someбез аргументов. Каждый кусок будет содержать как минимум один байт, а цикл автоматически завершится при достижении конца файла.-
Ожидает появления данных в потоке и возвращает часть из них.
Возвращаемое значение
b""(пустая строка байтов) указывает на достижение конца файла. Реализации должны гарантировать возвратb""только в том случае, если поток достиг конца файла!-
max_bytes (int) – Максимальное количество байтов для возврата. Должно быть больше нуля. Необязательно; если опущено, объект потока может выбрать разумное значение по умолчанию.
-
Полученные данные.
-
trio.BusyResourceError – если две задачи пытаются вызвать
receive_some()на одном потоке одновременно.trio.BrokenResourceError – если что-то пошло не так и поток повреждён.
trio.ClosedResourceError – если вы ранее закрыли этот объект потока или другая задача закрыла этот объект потока во время выполнения
receive_some().
Параметры:
Возвращаемое значение:
Тип возвращаемого значения:
Исключения:
-
abstractmethod await receive_some(max_bytes: int | None = None) → bytes | bytearray -
class trio.abc.ReceiveStream
-
Bases:
SendStream,ReceiveStreamСтандартный интерфейс для взаимодействия с двунаправленными потоками байтов.
Объект
Streamреализует как интерфейсSendStream, так иReceiveStream.При реализации этого интерфейса стоит задуматься, возможно ли реализовать
HalfCloseableStream.
class trio.abc.Stream
-
Базы:
StreamЭтот интерфейс расширяет
Stream, позволяя также закрывать часть отправки потока без закрытия части получения.-
Отправить индикатор конца файла в этом потоке, если это возможно.
Различие между
send_eof()иaclose()заключается в том, чтоsend_eof()— это однонаправленный индикатор конца файла. После вызова этого метода не следует пытаться отправлять больше данных по этому потоку, и ваш удалённый партнёр должен получить индикатор конца файла (в конечном итоге, после получения всех данных, отправленных вами до этого). Однако они могут продолжать отправлять вам данные, и вы можете продолжить их получать, вызываяreceive_some(). Можно представить это как вызовaclose()только для частиSendStreamобъекта потока (и, на самом деле, именно такtrio.StapledStreamэто реализует).Примеры:
В сокете это соответствует
shutdown(..., SHUT_WR)(страница man).Протокол SSH предоставляет возможность мультиплексирования двунаправленных «каналов» поверх одного зашифрованного соединения. Реализация SSH с Trio может экспонировать эти каналы как объекты
HalfCloseableStream, а вызовsend_eof()отправит запросSSH_MSG_CHANNEL_EOF(см. RFC 4254 §5.3).В SSL/TLS-зашифрованном соединении протокол не предоставляет никакого способа выполнить однонаправленное закрытие без полного закрытия соединения, поэтому
SSLStreamреализуетStream, а неHalfCloseableStream.
Если EOF уже отправлен, то этот метод должен успешно завершиться без действий.
-
trio.BusyResourceError — если другая задача уже выполняет
send_all(),wait_send_all_might_not_block()илиsend_eof()в этом потоке.trio.BrokenResourceError — если что-то пошло не так и поток сломан.
trio.ClosedResourceError — если вы ранее закрыли этот объект потока или другая задача закрыла этот объект потока во время выполнения
send_eof().
Исключения:
abstractmethod await send_eof() → None -
class trio.abc.HalfCloseableStream
-
Базы:
AsyncResource,Generic[T_resource]Стандартный интерфейс для прослушивания входящих подключений.
Объекты
Listenerтакже реализуют интерфейсAsyncResource, поэтому их можно закрыть, вызвавaclose()или с помощью блокаasync with.-
Подождите, пока не придёт входящее соединение, и верните его.
-
Объект, представляющий входящее соединение. На практике это обычно какой-то
Stream, но в принципе можно определитьListener, который возвращает, например, объекты каналов. -
trio.BusyResourceError — если две задачи пытаются вызвать
accept()на одном прослушивателе одновременно.trio.ClosedResourceError — если вы ранее закрыли этот объект прослушивателя или другая задача закрыла этот объект прослушивателя во время выполнения
accept().
Возвращает:
Тип возвращаемого значения:
Исключения:
Прослушиватели обычно не поднимают
BrokenResourceError, потому что для прослушивателей нет общего условия «сеть/удаленный партнёр разорвал соединение», которое можно обработать универсальным способом, как это есть для потоков. Другие ошибки могут возникнуть и быть подняты изaccept()— например, если у вас закончились дескрипторы файлов, то вы можете получитьOSErrorс установленным errno наEMFILE. -
abstractmethod await accept() → T_resource -
class trio.abc.Listener
-
Bases:
AsyncResource,Generic[SendType]Стандартный интерфейс для отправки объектов Python получателю.
SendChannelобъекты также реализуют интерфейсAsyncResource, поэтому их можно закрыть, вызвавacloseили используя блокasync with.Если вы хотите отправить сырые байты, а не объекты Python, см.
SendStream.-
Попытка отправить объект по каналу, блокируя, если необходимо.
-
значение (объект) – Объект для отправки.
-
trio.BrokenResourceError – если что-то пошло не так, и канал сломан. Например, вы можете получить это, если получатель уже закрыт.
trio.ClosedResourceError – если вы ранее закрыли этот
SendChannelобъект или если другой процесс закрывает его во время выполненияsend().trio.BusyResourceError – некоторые каналы позволяют нескольким процессам вызывать
sendодновременно, а другие нет. Если вы попытаетесь вызватьsendодновременно из нескольких процессов на канале, который этого не поддерживает, то вы можете получитьBusyResourceError.
Параметры:
Исключения:
-
abstractmethod await send(value: SendType) → None -
class trio.abc.SendChannel
-
Bases:
AsyncResource,Generic[ReceiveType]Стандартный интерфейс для получения объектов Python от отправителя.
Вы можете перебирать
ReceiveChannelс помощью циклаasync for:async for value in receive_channel: ...Это эквивалентно многократному вызову
receive(). Цикл завершается без ошибки, когдаreceiveвызываетEndOfChannel.ReceiveChannelобъекты также реализуют интерфейсAsyncResource, поэтому их можно закрыть, вызвавacloseили используя блокasync with.Если вы хотите получить сырые байты, а не объекты Python, см.
ReceiveStream.-
Попытка получить входящий объект, блокируя, если необходимо.
-
Полученный объект.
-
trio.EndOfChannel – если отправитель был закрыт корректно, и больше объектов не поступает. Это не состояние ошибки.
trio.ClosedResourceError – если вы ранее закрыли этот
ReceiveChannelобъект.trio.BrokenResourceError – если что-то пошло не так, и канал сломан.
trio.BusyResourceError – некоторые каналы позволяют нескольким процессам вызывать
receiveодновременно, а другие нет. Если вы попытаетесь вызватьreceiveодновременно из нескольких процессов на канале, который этого не поддерживает, то вы можете получитьBusyResourceError.
Возвращает:
Тип возвращаемого значения:
Исключения:
-
abstractmethod await receive() → ReceiveType -
class trio.abc.ReceiveChannel
-
Bases:
SendChannel[T],ReceiveChannel[T]Стандартный интерфейс для взаимодействия с двунаправленными каналами.
Channelэто объект, который реализует как интерфейсSendChannel, так иReceiveChannel, поэтому вы можете как отправлять, так и получать объекты.
class trio.abc.Channel
Общие инструменты для потоков
В настоящее время Trio предоставляет общий помощник для написания серверов, которые слушают подключения с использованием одного или нескольких Listener, и общий служебный класс для работы с потоками. И если вы хотите протестировать код, написанный с учетом интерфейса потоков, вы также можете ознакомиться с Потоками в trio.testing.
-
Слушать входящие подключения на
listeners, и для каждого из них запускать задачу, выполняющуюhandler(stream).Предупреждение
Если
handlerвызывает исключение, то эта функция ничего не делает для его перехвата — поэтому по умолчанию исключение будет распространяться и приведет к сбою вашего сервера. Если вы этого не хотите, перехватите исключения внутри вашегоhandlerили используйте объектhandler_nursery, который обрабатывает исключения другим способом.-
handler – Асинхронная вызываемая функция, которая будет вызываться как
handler_nursery.start_soon(handler, stream)для каждого входящего подключения.listeners – Список объектов
Listener.serve_listeners()несет ответственность за их закрытие.handler_nursery – Nursery, используемый для запуска обработчиков, или любой объект с методом
start_soon. ЕслиNone(по умолчанию), тоserve_listeners()создаст новую nursery внутри и будет использовать её.task_status – Эта функция может использоваться с
nursery.start, которая вернётlisteners.
-
Эта функция никогда не возвращает значение, пока не отменена.
Параметры:
Возвращаемое значение:
Обработка ресурсов:
Если
handlerне закрываетstream, то он будет закрыт с помощьюtrio.aclose_forcefully().Обработка ошибок:
Большинство ошибок, поступающих из
accept(), допускается распространять (приводя к сбою сервера). Однако некоторые ошибки — те, которые указывают на временную перегрузку сервера — обрабатываются специально. ЭтоOSErrorс одним из следующих errno:EMFILE: у процесса закончились дескрипторы файловENFILE: у системы закончились дескрипторы файловENOBUFS,ENOMEM: ядро столкнулось с какой-то проблемой ограничений памяти при попытке создать объект сокета
Когда
serve_listeners()получает одну из этих ошибок, то:Записывает ошибку в стандартный логгер
trio.serve_listeners(уровень = ERROR, с информацией об исключении). По умолчанию это приводит к её выводу в stderr.Ждет 100 мс перед повторным вызовом
accept, в надежде на восстановление системы.
-
await trio.serve_listeners(handler: Callable[[StreamT], Awaitable[object]], listeners: list[ListenerT], *, handler_nursery: Nursery | None = None, task_status: TaskStatus[list[ListenerT]] = TASK_STATUS_IGNORED) → NoReturn
-
Bases:
HalfCloseableStream,Generic[SendStreamT,ReceiveStreamT]Этот класс склеивает два однонаправленных потока, чтобы создать двунаправленный поток.
-
send_stream (SendStream) – Поток для отправки данных.
receive_stream (ReceiveStream) – Поток для получения данных.
Параметры:
Пример
Глупый способ создать поток, который эхом возвращает всё, что вы в него вводите:
left, right = trio.testing.memory_stream_pair() echo_stream = StapledStream(SocketStream(left), SocketStream(right)) await echo_stream.send_all(b"x") assert await echo_stream.receive_some() == b"x"
StapledStreamобъекты реализуют методы интерфейсаHalfCloseableStream. У них также есть два дополнительных общедоступных атрибута:-
Подлежащий
SendStream.send_all()иwait_send_all_might_not_block()делегированы этому объекту.
send_stream-
Подлежащий
ReceiveStream.receive_some()делегированы этому объекту.
receive_stream-
Вызывает
acloseдля обоих подлежащих потоков.
await aclose() → None-
Вызывает
self.receive_stream.receive_some.
await receive_some(max_bytes: int | None = None) → bytes-
Вызывает
self.send_stream.send_all.
await send_all(data: bytes | bytearray | memoryview) → None-
Закрывает сторону отправки потока.
Если
self.send_stream.send_eof()существует, то этот вызов. Иначе это вызовself.send_stream.aclose().
await send_eof() → None-
Вызывает
self.send_stream.wait_send_all_might_not_block.
await wait_send_all_might_not_block() → None -
class trio.StapledStream(send_stream: SendStreamT, receive_stream: ReceiveStreamT)
Сокеты и сетевое взаимодействие
Интерфейс высокого уровня для работы с сетью построен на основе абстракции потока.
-
Подключение к указанному хосту и порту по протоколу TCP.
Если у заданного
hostассоциировано несколько IP-адресов, возникает проблема: какой использовать?Один подход — пытаться подключиться к первому адресу, а затем, если это не удаётся, к второму и так далее, пока не будут испробованы все. Но проблема в том, что если первый IP-адрес недоступен (например, это IPv6-адрес, а наша сеть отбрасывает IPv6-пакеты), то мы можем потратить десятки секунд на ожидание таймаута первого подключения, прежде чем перейти ко второму адресу.
Другой подход — попытаться подключиться ко всем адресам одновременно, параллельно, и использовать первое успешное подключение, отказываясь от остальных. Это будет быстро, но создаст излишнюю нагрузку на сеть и удалённый сервер.
Данная функция балансирует между этими двумя крайностями: она обрабатывает доступные адреса по одному, как в первом подходе; но, если прошло
happy_eyeballs_delayсекунд, и подключение всё ещё ожидается, то она теряет терпение и начинает следующую попытку подключения параллельно. Как только одна попытка подключения окажется успешной, все остальные попытки отменяются. Это позволяет избежать излишней нагрузки, поскольку большинство подключений происходит после одной или двух попыток, но если один из адресов недоступен, это не замедляет нас слишком сильно.Это известно как алгоритм «счастливых глаз», и наша конкретная реализация моделируется под тем, как Chrome подключается к веб-серверам; см. RFC 6555 для получения дополнительной информации.
-
host (str или bytes) — Хост для подключения. Может быть IPv4-адресом, IPv6-адресом или именем хоста.
port (int) — Порт для подключения.
happy_eyeballs_delay (float или None) — Количество секунд ожидания каждой попытки подключения для успеха или неудачи, прежде чем начать другую попытку параллельно. Устанавливается в значение
None, если нужно ограничиться только одной попыткой подключения (как вsocket.create_connection()). Значение по умолчанию: 0.25 (250 мс).-
local_address (None или str) —
Локальный IP-адрес или имя хоста, используемый в качестве источника исходящих подключений. Если
None, ОС выбирает IP-адрес источника.Это полезно в некоторых экзотических сетевых конфигурациях, где у хоста несколько IP-адресов, и требуется принудительно использовать определённый.
Обратите внимание, что если вы передаёте IPv4
local_address, то не сможете подключиться к IPv6-хостам, и наоборот. Если вы хотите воспользоваться этим для принудительного использования IPv4 или IPv6 без указания точного адреса источника, вы можете использовать IPv4-адрес маскированияlocal_address="0.0.0.0"или IPv6-адрес маскированияlocal_address="::".
-
объект
Stream, подключенный к указанному серверу. -
OSError — если подключение не удаётся.
Параметры:
Возвращаемое значение:
Тип возвращаемого значения:
Исключения:
См. также
open_ssl_over_tcp_stream
-
await trio.open_tcp_stream(host: str | bytes, port: int, *, happy_eyeballs_delay: float | None = 0.25, local_address: str | None = None) → SocketStream
-
Принимает входящие TCP-соединения и для каждого запускает задачу, выполняющую
handler(stream).Это тонкий оберточный модуль над
open_tcp_listeners()иserve_listeners()— см. их для получения подробной информации.Предупреждение
Если
handlerвызывает исключение, эта функция ничего не делает, чтобы его перехватить — поэтому по умолчанию исключение будет передано и приведёт к сбою вашего сервера. Если вы этого не хотите, перехватывайте исключения внутри вашейhandlerили используйте объектhandler_nursery, который реагирует на исключения каким-то другим способом.При использовании с
nursery.startвы получаете вновь открытые слушатели. Например, если вы хотите запустить сервер в наборе тестов и затем подключиться к нему, чтобы проверить, что он работает правильно, вы можете использовать что-то вроде:from trio import SocketListener, SocketStream from trio.testing import open_stream_to_socket_listener async with trio.open_nursery() as nursery: listeners: list[SocketListener] = await nursery.start(serve_tcp, handler, 0) client_stream: SocketStream = await open_stream_to_socket_listener(listeners[0]) # Then send and receive data on 'client_stream', for example: await client_stream.send_all(b"GET / HTTP/1.0\r\n\r\n")Это предотвращает несколько распространённых проблем:
Это позволяет ядру выбрать случайный свободный порт, поэтому набор тестов не зависит от конкретного открытого порта.
Это ожидает, пока сервер не начнёт принимать подключения на этом порте, прежде чем
startвернёт значение, таким образом исключая гонку, при которой входящее соединение поступает до того, как сервер будет готов.Это использует объект Listener, чтобы узнать выбранный порт, поэтому он может подключиться к правильному месту.
-
handler — Обработчик, который нужно запустить для каждого входящего подключения. Передаётся в
serve_listeners().port — Порт для прослушивания. Используйте 0, чтобы ядро выбрало свободный порт. Передаётся в
open_tcp_listeners().host (str, bytes или None) — Интерфейс хоста для прослушивания; используйте
Noneдля привязки к адресной маске. Передаётся вopen_tcp_listeners().backlog — Буфер ожидающих подключений, или None для выбора подходящего по умолчанию. Передаётся в
open_tcp_listeners().handler_nursery — Детский сад для запуска обработчиков или None для использования внутреннего. Передаётся в
serve_listeners().task_status — Эта функция может быть использована с
nursery.start.
-
Эта функция возвращает только при отмене.
Параметры:
Возвращаемое значение:
await trio.serve_tcp(handler: Callable[[trio.SocketStream], Awaitable[object]], port: int, *, host: str | bytes | None = None, backlog: int | None = None, handler_nursery: trio.Nursery | None = None, task_status: TaskStatus[list[trio.SocketListener]] = TASK_STATUS_IGNORED) → None
-
Установите зашифрованное соединение TLS с указанным хостом и портом по протоколу TCP.
Это удобная обертка, которая вызывает
open_tcp_stream()и оборачивает результат вSSLStream.Эта функция не выполняет рукопожатие TLS; вы можете сделать это вручную, вызвав
do_handshake(), или же оно будет выполнено автоматически при первом отправлении или получении данных.-
host (bytes или str) – Хост, к которому нужно подключиться. Сервер должен иметь действительный сертификат TLS для этого имени хоста.
port (int) – Порт для подключения.
https_compatible (bool) – Установите значение True, если вы подключаетесь к веб-серверу. Подробнее см.
SSLStream. По умолчанию: False.ssl_context (
SSLContextили None) – Контекст SSL для использования. Если None (по умолчанию), будет вызванssl.create_default_context()для создания контекста.happy_eyeballs_delay (float) – См.
open_tcp_stream().
-
зашифрованное соединение с сервером.
Параметры:
Возвращаемое значение:
Тип возвращаемого значения:
-
await trio.open_ssl_over_tcp_stream(host: str | bytes, port: int, *, https_compatible: bool = False, ssl_context: ssl.SSLContext | None = None, happy_eyeballs_delay: float | None = 0.25) → trio.SSLStream[SocketStream]
-
Принимать входящие подключения TCP и для каждого из них запускать задачу, выполняющую
handler(stream).Это тонкая удобная обертка вокруг
open_ssl_over_tcp_listeners()иserve_listeners()– см. их для получения подробной информации.Предупреждение
Если
handlerвызывает исключение, то эта функция не делает ничего особенного для его перехвата – поэтому по умолчанию исключение будет распространено и завершит работу вашего сервера. Если этого не хотите, перехватывайте исключения внутриhandlerили используйте объектhandler_nursery, который каким-то образом обрабатывает исключения.При использовании с
nursery.startвы получаете вновь открытые слушатели. Смотрите документацию дляserve_tcp()для примера, где это полезно.-
handler – Обработчик, который нужно запустить для каждого входящего подключения. Передаётся в
serve_listeners().port (int) – Порт для прослушивания. Используйте 0, чтобы ядро выбрало открытый порт. В конечном итоге передаётся в
open_tcp_listeners().ssl_context (SSLContext) – Контекст SSL, который нужно использовать для всех входящих подключений. Передаётся в
open_ssl_over_tcp_listeners().host (str, bytes или None) – Адрес для привязки; используйте
Noneдля привязки к универсальному адресу. В конечном итоге передаётся вopen_tcp_listeners().https_compatible (bool) – Установите значение True, если хотите использовать TLS в стиле «HTTPS». Подробнее см.
SSLStream.handler_nursery – Nursery для запуска обработчиков или None для использования внутренней nursery. Передаётся в
serve_listeners().task_status – Эта функция может быть использована с
nursery.start.
-
Функция возвращает только при отмене.
Параметры:
Возвращаемое значение:
-
await trio.serve_ssl_over_tcp(handler: Callable[[trio.SSLStream[SocketStream]], Awaitable[object]], port: int, ssl_context: ssl.SSLContext, *, host: str | bytes | None = None, https_compatible: bool = False, backlog: int | None = None, handler_nursery: trio.Nursery | None = None, task_status: trio.TaskStatus[list[trio.SSLListener[SocketStream]]] = TASK_STATUS_IGNORED) → NoReturn
-
Открывает соединение с указанным сокет Unix-домена.
У вас должны быть права чтения/записи в указанном файле для подключения.
-
filename (str или bytes) – Имя файла для открытия соединения.
-
Stream, подключённый к указанному файлу. -
OSError – Если сокет файла не удалось подключиться.
RuntimeError – Если сокеты AF_UNIX не поддерживаются.
Параметры:
Возвращаемое значение:
Тип возвращаемого значения:
Исключения:
-
await trio.open_unix_socket(filename: str | bytes | PathLike[str] | PathLike[bytes]) → SocketStream
-
Наследуется от
HalfCloseableStreamРеализация интерфейса
trio.abc.HalfCloseableStreamна основе сырого сетевого сокета.-
socket – Объект сокета Trio для обертывания. Должен быть типа
SOCK_STREAMи подключен.
Параметры:
По умолчанию для TCP-сокетов
SocketStreamвключаетTCP_NODELAYи (на платформах, где это поддерживается) включаетTCP_NOTSENT_LOWATс разумным размером буфера (в настоящее время 16 КБ) – см. вопрос #72 для обсуждения. Конечно, вы можете переопределить эти значения по умолчанию, вызвавsetsockopt().После создания объекта
SocketStreamон реализует весь интерфейсtrio.abc.HalfCloseableStream. Кроме того, он предоставляет несколько дополнительных функций:-
Объект сокета Trio, который оборачивает это потоковое соединение.
socketawait aclose() → None-
Проверяет текущее значение параметра для сокета.
См.
socket.socket.getsockopt()для деталей.
getsockopt(level: int, option: int, buffersize: int = 0) → int | bytesawait receive_some(max_bytes: int | None = None) → bytesawait send_all(data: bytes | bytearray | memoryview) → Noneawait send_eof() → None-
Устанавливает параметр для сокета.
См.
socket.socket.setsockopt()для деталей.
setsockopt(level: int, option: int, value: int | Buffer | None, length: int | None = None) → Noneawait wait_send_all_might_not_block() → None -
Потоковое подключение к сокету
-
Наследуется от
Listener[SocketStream]Слушатель
Listenerиспользует сокет для прослушивания входящих подключений в виде объектовSocketStream.-
socket – Объект сокета Trio для обертывания. Должен быть типа
SOCK_STREAMи прослушивать.
Параметры:
Обратите внимание, что
SocketListener«принимает на себя ответственность» за предоставленный сокет; закрытиеSocketListenerтакже закроет сокет.-
Объект сокета Trio, который оборачивает это потоковое соединение.
socket-
Принимает входящее подключение.
-
OSError – если базовый вызов
acceptвызывает неожиданную ошибку.ClosedResourceError – если вы уже закрыли сокет.
Возвращает:
Исключения:
Этот метод обрабатывает обычные ошибки, такие как
ECONNABORTED, но передает другие ошибки своему вызывающему объекту. В частности, он не делает никаких особых усилий для обработки ошибок истощения ресурсов, таких какEMFILE,ENFILE,ENOBUFS,ENOMEM.
await accept() → SocketStream-
Закрыть этот слушатель и его базовый сокет.
await aclose() → None -
class trio.SocketListener(socket: SocketType)
-
Создаёт объекты
SocketListenerдля прослушивания TCP-подключений.-
-
port (int) –
Порт для прослушивания.
Если вы используете 0 в качестве порта, ядро автоматически выберет произвольный свободный порт. Но будьте осторожны: если вы используете эту функцию при привязке к нескольким IP-адресам, то каждый IP-адрес получит свой случайный порт, и возвращённые слушатели, вероятно, будут прослушивать на разных портах. В частности, это произойдёт, если вы используете
host=None(что является значением по умолчанию), потому что в этом случаеopen_tcp_listeners()будет привязываться как к IPv4-маркеру (0.0.0.0), так и к IPv6-маркеру (::). -
Локальный интерфейс для привязки. Передаётся в
getaddrinfo()с установленным флагомAI_PASSIVE.Если вы хотите привязаться к маркеру адреса для IPv4 и IPv6, чтобы принять подключения на всех доступных интерфейсах, передайте
None. Это значение по умолчанию.Если вам нужен конкретный интерфейс, передайте его IP-адрес или имя хоста. Если имя хоста разрешается до нескольких IP-адресов, эта функция откроет по одному слушателю на каждом из них.
Если вы хотите использовать только IPv4 или только IPv6, но хотите принимать подключения на всех интерфейсах, передайте семейно-специфический маркер адреса:
"0.0.0.0"для только IPv4 и"::"для только IPv6. backlog (int or None) – Размер очереди ожидающих соединений. Если оставить
None, Trio выберет хорошее значение по умолчанию. (В настоящее время: значение, настроенное вашей системой как максимальный размер очереди.)
-
-
Список объектов
SocketListener
Параметры:
Возвращает:
Исключения:
-
await trio.open_tcp_listeners(port: int, *, host: str | bytes | None = None, backlog: int | None = None) → list[SocketListener]
-
Начинает прослушивание SSL/TLS-шифрованных TCP-соединений на заданном порту.
-
port (int) – Порт для прослушивания. См.
open_tcp_listeners().ssl_context (SSLContext) – Контекст SSL, который будет использоваться для всех входящих соединений.
host (str, bytes, или None) – Адрес для привязки; используйте
Noneдля привязки к адресу подстановки. См.open_tcp_listeners().backlog (int или None) – Подробности см. в
open_tcp_listeners().
Параметры:
-
await trio.open_ssl_over_tcp_listeners(port: int, ssl_context: ssl.SSLContext, *, host: str | bytes | None = None, https_compatible: bool = False, backlog: int | None = None) → list[trio.SSLListener[SocketStream]]
Поддержка SSL/TLS
Trio предоставляет поддержку SSL/TLS, основанную на стандартном модуле ssl. Классы Trio SSLStream и SSLListener получают свою конфигурацию из ssl.SSLContext, который можно создать с помощью ssl.create_default_context() и настроить с помощью других констант и функций в модуле ssl.
Предупреждение
Избегайте непосредственного создания объекта
ssl.SSLContext. У только что созданногоSSLContextболее слабые настройки безопасности, чем у объекта, возвращаемого функциейssl.create_default_context().
Вместо использования ssl.SSLContext.wrap_socket(), создайте объект SSLStream:
-
Bases:
Stream,Generic[T_Stream]Шифрованное общение с использованием SSL/TLS.
SSLStreamоборачивает произвольныйStreamи позволяет вам выполнять шифрованное общение по нему, используя обычный интерфейсStream. Вы передаёте обычные данные вsend_all(), затем он шифрует их и отправляет зашифрованные данные по базовомуStream;receive_some()извлекает зашифрованные данные из базовогоStreamи расшифровывает их перед возвратом.Перед использованием этого класса внимательно ознакомьтесь с документацией стандартной библиотеки
ssl, а также, вероятно, с другой общей документацией по SSL/TLS. SSL/TLS тонкий и быстро сердится. Серьёзно. Я не шучу.-
transport_stream (Поток) – Поток, используемый для передачи зашифрованных данных. Требуется.
ssl_context (SSLContext) –
SSLContext, используемый для этого соединения. Требуется. Обычно создаётся вызовомssl.create_default_context().server_hostname (str, bytes, или None) – Имя сервера, с которым производится подключение. Используется для SNI и для проверки сертификата сервера (если проверка по имени включена). Это фактически обязательно для клиентов и, на самом деле, обязательно, если
ssl_context.check_hostnameявляетсяTrue.server_side (bool) – Является ли этот поток клиентом или сервером. По умолчанию False, то есть режим клиента.
-
https_compatible (bool) –
Существуют две версии SSL/TLS, которые часто встречаются в дикой природе: стандартная версия и версия, используемая для HTTPS (HTTP-over-SSL/TLS).
Стандартные реализации SSL/TLS всегда отправляют криптографически подписанное
close_notifyсообщение перед закрытием соединения. Это важно, потому что если базовый транспорт просто закроется, то другой стороне не будет известно, было ли соединение намеренно закрыто партнёром, с которым они договорились о криптографическом соединении, или каким-то злоумышленником-«человеком посередине», который не может манипулировать криптографическим потоком, но может манипулировать транспортным уровнем (так называемая «атака обрыва»).Однако эта часть стандарта широко игнорируется реальными реализациями HTTPS, что означает, что если вы хотите с ними взаимодействовать, то вам тоже нужно это игнорировать.
К счастью, это не так плохо, как звучит, потому что протокол HTTP уже включает собственный эквивалент
close_notify, поэтому повторное выполнение этого на уровне SSL/TLS избыточно. Но не все протоколы это делают! Поэтому по умолчанию Trio реализует более безопасную стандартную версию (https_compatible=False). Но если вы общаетесь по протоколу HTTPS или по другому протоколу, гдеclose_notifyчасто пропускаются, то вы должны установитьhttps_compatible=True; с этой установкой Trio не будет ожидать и не будет отправлятьclose_notifyсообщения.Если у вас есть код, который был написан для использования
ssl.SSLSocket, а теперь вы переносите его в Trio, то вам может быть полезно знать, что различие междуSSLStreamиssl.SSLSocketсостоит в том, чтоSSLSocketреализует поведениеhttps_compatible=Trueпо умолчанию.
Параметры:
-
Базовый транспортный поток, который был передан в
__init__. Примером, когда это может быть полезно, является использованиеSSLStreamчерезSocketStreamи вызов методаSocketStream’ssetsockopt().Тип:
transport_streamВнутренне этот класс реализован с помощью экземпляра
ssl.SSLObject, и все методы и атрибутыSSLObjectповторно экспортируются как методы и атрибуты в этом классе. Однако есть одно различие:SSLObjectимеет несколько методов, которые возвращают информацию о зашифрованном соединении, такие какcipher()илиselected_alpn_protocol(). Если вы вызываете их до установления рукопожатия, когда они не могут вернуть полезные данные, тоssl.SSLObjectвозвращает None, ноtrio.SSLStreamвызываетNeedHandshakeError.Это также означает, что если вы регистрируете обратный вызов SNI с помощью
sni_callback, то первый аргумент, который получает ваш обратный вызов, будетssl.SSLObject.-
Вежливо завершить это соединение и закрыть базовый транспорт.
Если
https_compatibleравно False (по умолчанию), то это пытается сначала отправитьclose_notify, а затем закрыть базовый поток, вызвав его методaclose().Если
https_compatibleустановлено в True, то это просто закрывает базовый поток и отмечает этот поток как закрытый.
await aclose() → None -
class trio.SSLStream(transport_stream: T_Stream, ssl_context: SSLContext, *, server_hostname: str | bytes | None = None, server_side: bool = False, https_compatible: bool = False)
-
Убедитесь, что начальный обмен рукопожатиями завершен.
Протокол SSL требует начального обмена рукопожатиями для обмена сертификатами, выбора криптографических ключей и т. д., прежде чем можно будет отправлять или получать какие-либо данные. Вам не нужно вызывать этот метод; если вы этого не сделаете, то
SSLStreamавтоматически выполнит обмен рукопожатиями по мере необходимости, в первый раз, когда вы попытаетесь отправить или получить данные. Но если вы хотите запустить его вручную — например, потому что хотите посмотреть сертификат удаленного узла, прежде чем начать с ним общаться — тогда вы можете вызвать этот метод.Если начальный обмен рукопожатиями уже выполняется в другой задаче, это ожидает его завершения, а затем возвращается.
Если начальный обмен рукопожатиями уже завершен, это возвращается немедленно, ничего не делая (кроме выполнения контрольной точки).
Предупреждение
Если этот метод отменён, то он может оставить
SSLStreamв непригодном для использования состоянии. Если это произойдёт, то любая последующая попытка использовать объект вызоветtrio.BrokenResourceError.
await do_handshake() → None-
Прочтите некоторые данные из базового транспорта, расшифруйте их и верните.
См.
trio.abc.ReceiveStream.receive_some()для получения подробной информации.Предупреждение
Если этот метод отменён во время выполнения начального обмена рукопожатиями или повторного согласования, то он может оставить
SSLStreamв непригодном для использования состоянии. Если это произойдёт, то любая последующая попытка использования объекта вызоветtrio.BrokenResourceError.
await receive_some(max_bytes: int | None = None) → bytes | bytearray-
Зашифруйте некоторые данные, а затем отправьте их по базовому транспорту.
См.
trio.abc.SendStream.send_all()для получения подробной информации.Предупреждение
Если этот метод отменён, то он может оставить
SSLStreamв непригодном для использования состоянии. Если это произойдёт, любая попытка использовать объект вызоветtrio.BrokenResourceError.
await send_all(data: bytes | bytearray | memoryview) → None-
Чисто закрыть уровень шифрования SSL/TLS, позволив базовому потоку использоваться для нешифрованного обмена.
Вам, скорее всего, это не нужно.
-
Пару
(transport_stream, trailing_bytes), гдеtransport_stream— это базовый поток транспорта, аtrailing_bytes— строка байтов. ПосколькуSSLStreamне обязательно знает, где будет конец зашифрованных данных, может случиться, что он случайно прочтёт слишком много из базового потока.trailing_bytesсодержит эти дополнительные данные; вы должны обработать их так, как будто они были возвращены из вызоваtransport_stream.receive_some(...).
Возвращает:
-
await unwrap() → tuple[Stream, bytes | bytearray]await wait_send_all_might_not_block() → None-
И если вы реализуете сервер, вы можете использовать SSLListener:
-
Базируется на
Listener[SSLStream[T_Stream]]A
Listenerдля SSL/TLS-зашифрованных серверов.SSLListenerобертывает другой Listener и преобразует все входящие соединения в зашифрованные соединения, обертывая их вSSLStream.-
transport_listener (Listener) — Слушатель, входящие соединения которого будут обернуты в
SSLStream.ssl_context (SSLContext) —
SSLContext, который будет использоваться для входящих соединений.
Параметры:
-
Базовый слушатель, который был передан в
__init__.Тип:
transport_listener-
Принимает следующее соединение и оборачивает его в
SSLStream.См.
trio.abc.Listener.accept()для получения подробной информации.
await accept() → SSLStream[T_Stream]-
Закрыть слушателя транспорта.
await aclose() → None -
class trio.SSLListener(transport_listener: Listener[T_Stream], ssl_context: SSLContext, *, https_compatible: bool = False)
Некоторые методы SSLStream поднимают NeedHandshakeError, если вы их вызываете до завершения обмена рукопожатиями:
-
Некоторые методы
SSLStreamне могут вернуть осмысленные данные до завершения обмена рукопожатиями. Если вы их вызываете до обмена рукопожатиями, они поднимают эту ошибку.
exception trio.NeedHandshakeError
Поддержка DTLS
В Trio также поддерживается Datagram TLS (DTLS), который похож на TLS, но предназначен для ненадежных UDP-соединений. Это может быть полезно для приложений, где надёжная доставка TCP в определённом порядке проблематична, например, для видеоконференций, игр с низкой задержкой и VPN.
В настоящее время для использования DTLS с Trio требуется PyOpenSSL. В будущем мы надеемся также разрешить использование модуля stdlib ssl, но к сожалению, это пока невозможно.
Предупреждение
Обратите внимание, что PyOpenSSL в значительной степени является более низкоуровневым модулем, чем
ssl. В частности, в настоящее время он НЕ ИМЕЕТ ВСТРОЕННОГО МЕХАНИЗМА ПРОВЕРКИ СЕРТИФИКАТОВ. Мы настоятельно рекомендуем использовать библиотеку service-identity для проверки имен хостов и сертификатов.
-
Конечная точка DTLS.
Один UDP-сокет может обрабатывать произвольное количество одновременных DTLS-соединений, выступая в качестве клиента или сервера по мере необходимости. Объект
DTLSEndpointсодержит UDP-сокет и управляет этими соединениями, которые представлены объектамиDTLSChannel.-
socket – (trio.socket.SocketType): Сокет
SOCK_DGRAM. Если вы хотите принимать входящие соединения в режиме сервера, то, вероятно, вам нужно привязать сокет к известному порту.incoming_packets_buffer (int) – Каждый
DTLSChannel, использующий этот сокет, имеет свой собственный буфер, который хранит входящие пакеты до тех пор, пока вы не вызоветеreceiveдля их чтения. Это позволяет вам изменять размер этого буфера.statisticsпозволяет проверить, переполнен ли буфер.
Параметры:
-
Оба аргумента конструктора также доступны как атрибуты, на случай если вам нужно получить к ним доступ позже.
socketincoming_packets_buffer-
Инициализация исходящего DTLS-соединения.
Обратите внимание, что этот метод синхронный. Это потому, что он не фактически не инициирует никакого ввода/вывода — он просто создаёт объект
DTLSChannel. Фактический обмен рукопожатием не происходит до тех пор, пока вы не начнёте использоватьDTLSChannel. Это даёт вам возможность предварительно выполнить дополнительные настройки, такие как установка MTU и т. д.-
address – Адрес для подключения. Обычно кортеж (хост, порт), например,
("127.0.0.1", 12345).ssl_context (OpenSSL.SSL.Context) – Объект контекста PyOpenSSL для этого соединения.
-
DTLSChannel
Параметры:
Возвращает:
-
connect(address: tuple[str, int], ssl_context: OpenSSL.SSL.Context) → DTLSChannel-
Прослушивает входящие соединения и порождает обработчик для каждого из них с помощью внутренней ячейки.
Аналогично
serve_tcp, эта функция никогда не возвращается, пока не отменена, илиDTLSEndpointне закрыта и все обработчики не завершат работу.Использование обычно выглядит так:
async def handler(dtls_channel): ... async with trio.open_nursery() as nursery: await nursery.start(dtls_endpoint.serve, ssl_context, handler) # ... do other things here ...Объект
dtls_channel, переданный в функцию обработчика, уже выполнил часть обмена «куки» DTLS-процедуры рукопожатия, поэтому адрес отправителя надёжен. Однако фактический криптографический обмен рукопожатием не происходит до тех пор, пока вы не начнёте его использовать, что даёт вам возможность выполнить любые последние настройки и возможность перехватить и обработать ошибки рукопожатия.-
ssl_context (OpenSSL.SSL.Context) – Объект контекста PyOpenSSL для входящих соединений.
async_fn – Функция обработчика, которая будет вызвана для каждого входящего соединения.
*args – Дополнительные аргументы для передачи в функцию обработчика.
Параметры:
-
await serve(ssl_context: OpenSSL.SSL.Context, async_fn: Callable[[DTLSChannel, Unpack[PosArgsT]], Awaitable[object]], *args: Unpack[PosArgsT], task_status: trio.TaskStatus[None] = TASK_STATUS_IGNORED) → None-
Закрыть этот сокет и все связанные DTLS-соединения.
Этот объект также может использоваться как менеджер контекста.
close() → None -
class trio.DTLSEndpoint(socket: SocketType, *, incoming_packets_buffer: int = 10)
-
Подключение DTLS.
У этого класса нет публичного конструктора — экземпляры получаются, вызывая
DTLSEndpoint.serveилиconnect.-
Используемый
DTLSEndpoint.
endpoint-
IP/порт удалённого узла, с которым связано это соединение.
peer_address-
Выполнить рукопожатие.
Вызов этого метода необязателен — он будет автоматически вызван при первом вызове
sendилиreceive. Но явный вызов может быть полезен, если требуется контроль таймаута повторной передачи, использование области отмены для установки общего таймаута на рукопожатие или перехват ошибок при рукопожатии.Вызов этого метода несколько раз или одновременный вызов из нескольких задач безопасен — первый вызов выполнит рукопожатие, а остальные будут недействительными.
-
initial_retransmit_timeout (float) –
Поскольку UDP — ненадежный протокол, возможно, что некоторые пакеты, отправленные во время рукопожатия, будут утеряны. Для обработки этого DTLS использует таймер для автоматической повторной передачи пакетов рукопожатия, которые не получают ответа. Это позволяет установить таймаут, используемый для обнаружения потери пакетов. В идеале он должен быть установлен примерно в 1,5 раза больше времени кругового обмена с удалённым узлом, но 1 секунда — разумное значение по умолчанию. Здесь есть полезные рекомендации: https://tlswg.org/dtls13-spec/draft-ietf-tls-dtls13.html#name-timer-values.
Это начальный таймаут, потому что если пакеты продолжают теряться, Trio автоматически будет увеличивать его значение, чтобы не перегружать сеть.
Параметры:
-
await do_handshake(*, initial_retransmit_timeout: float = 1.0) → None-
Отправить пакет данных безопасно.
await send(data: bytes) → None-
Получить следующий пакет данных от удалённого узла, ожидая при необходимости.
Это безопасно вызывать из нескольких задач одновременно, если есть такая необходимость. И что важнее, это безопасно для отмены, что означает, что отмена вызова
receiveникогда не приведёт к потере пакета или нарушению основного соединения.
await receive() → bytes-
Закрыть это соединение.
DTLSChannelна самом деле не владеют ресурсами на уровне ОС — сокет принадлежитDTLSEndpoint, а не отдельным соединениям. Поэтому вы не обязаны вызывать этот метод. Однако он прервёт любые другие задачи, вызывающиеreceive, сClosedResourceErrorи приведёт к отказу будущих попыток использования этого соединения.Также можно использовать этот объект как контекстный менеджер для синхронного или асинхронного использования.
close() → None-
Закрыть это соединение асинхронно.
Это включено для соответствия контракту
trio.abc.Channel. Оно идентичноclose, но асинхронное.
await aclose() → None-
Указывает Trio на максимальный объём данных, который может быть отправлен в одном пакете этому узлу.
Trio на самом деле не накладывает этого ограничения — если вы передадите большой пакет в
send, мы зашифруем его и попытаемся отправить. Но вызов этого метода имеет два полезных эффекта:Если вызван до выполнения рукопожатия, Trio автоматически фрагментирует сообщения рукопожатия, чтобы они вписались в заданный MTU. Он также может фрагментировать их ещё меньше, если обнаружит признаки потери пакетов, поэтому установка этого значения не требуется для успешного соединения. Но обнаружение потери пакетов происходит только после истечения нескольких таймаутов, поэтому если у вас есть основания полагать, что меньший MTU необходим, вы можете установить это значение, чтобы пропустить эти таймауты и быстрее установить соединение.
Он изменяет значение, возвращаемое из
get_cleartext_mtu. Поэтому, если у вас есть какое-то приближение к MTU на уровне сети, вы можете использовать это, чтобы определить, какой объём накладных расходов DTLS потребуется для хешей/заполнения/и т. д. и сколько места останется для данных вашего приложения.
Здесь MTU измеряет максимальный размер полезной нагрузки UDP, который, по вашему мнению, можно отправить, объём зашифрованных данных, которые можно передать операционной системе в одном вызове
send. Он не должен включать заголовки IP/UDP. Обратите внимание, что оценки MTU операционной системы часто являются MTU уровня канала связи, поэтому вам необходимо вычесть 28 байтов для IPv4 и 48 байтов для IPv6, чтобы получить MTU шифрованного текста.По умолчанию Trio предполагает MTU 1472 байта для IPv4 и 1452 байта для IPv6, которые соответствуют общему Ethernet MTU 1500 байт с учётом накладных расходов IP/UDP.
set_ciphertext_mtu(new_mtu: int) → None-
Возвращает максимальное количество байтов, которые можно передать в одном вызове
send, при этом вписываясь в MTU на уровне сети.Подробнее см.
set_ciphertext_mtu.
get_cleartext_mtu() → int-
Возвращает объект
DTLSChannelStatisticsсо статистикой по этому соединению.
statistics() → DTLSChannelStatistics -
class trio.DTLSChannel(*args: object, **kwargs: object)
-
В настоящее время он имеет только один атрибут:
incoming_packets_dropped_in_trio(int): Указывает количество входящих пакетов от этого узла, которые Trio успешно получил из сети, но затем потерял, потому что внутренний буфер канала был заполнен. Если это ненулевое значение, возможно, вам стоит чаще вызыватьreceive, использовать большийincoming_packets_bufferили просто не беспокоиться об этом, поскольку ваш протокол на основе UDP должен быть способен справиться с периодическими потерями пакетов, правильно?
class trio.DTLSChannelStatistics(incoming_packets_dropped_in_trio: int)
Сеть низкого уровня с trio.socket
Модуль trio.socket предоставляет базовый API для сетей низкого уровня Trio. Если вы выполняете обычные операции с потоковыми подключениями по IPv4/IPv6/сокет Unix-домена, то вам, вероятно, следует придерживаться API высокого уровня, описанного выше. Если вы хотите использовать UDP или экзотические семейства адресов, такие как AF_BLUETOOTH, или хотите получить прямой доступ ко всем особенностям API сетевого стека вашей системы, то вы попали по адресу.
Экспорт верхнего уровня
В целом, API, предоставляемый trio.socket, отражает API стандартной библиотеки socket. Большинство констант (например, SOL_SOCKET) и простых утилит (например, inet_aton()) просто повторно экспортируются без изменений. Однако есть и некоторые отличия, которые описаны здесь.
Во-первых, Trio предоставляет аналоги всем функциям стандартной библиотеки, которые возвращают объекты сокетов; их интерфейс идентичен, за исключением того, что они изменены для возврата объектов сокетов Trio:
-
Создаёт новый сокет Trio, как
socket.socket.Поведение этой функции можно настроить с помощью
set_custom_socket_factory().
trio.socket.socket(family=-1, type=-1, proto=-1, fileno=None)
-
Аналогично
socket.socketpair(), но возвращает пару объектов сокетов Trio.
trio.socket.socketpair(family=None, type=SocketKind.SOCK_STREAM, proto=0)
-
Аналогично
socket.fromfd(), но возвращает объект сокета Trio.
trio.socket.fromfd(fd, family, type, proto=0)
-
Аналогично
socket.fromshare(), но возвращает объект сокета Trio.
trio.socket.fromshare(data)
Кроме того, есть новая функция для прямого преобразования сокета стандартной библиотеки в сокет Trio:
-
Преобразует объект сокета стандартной библиотеки
socket.socketв объект сокета Trio.
trio.socket.from_stdlib_socket(sock: socket) → SocketType
В отличие от socket.socket, trio.socket.socket() — это функция, а не класс; если вы хотите проверить, является ли объект сокетом Trio, используйте isinstance(obj, trio.socket.SocketType).
Для поиска имён Trio предоставляет стандартные функции, но с некоторыми изменениями:
-
Поиск числового адреса по имени.
Аргументы и возвращаемые значения идентичны
socket.getaddrinfo(), за исключением того, что эта версия асинхронна.Кроме того,
trio.socket.getaddrinfo()корректно использует IDNA 2008 для обработки имён доменов, не состоящих из ASCII. (socket.getaddrinfo()использует IDNA 2003, что может давать неверный результат в некоторых случаях и привести к подключению к другому хосту, чем предполагалось; см. bpo-17305.)Поведение этой функции можно настроить с помощью
set_custom_hostname_resolver().
await trio.socket.getaddrinfo(host: bytes | str | None, port: bytes | str | int | None, family: int = 0, type: int = 0, proto: int = 0, flags: int = 0) → list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes]]]
-
Поиск имени по числовому адресу.
Аргументы и возвращаемые значения идентичны
socket.getnameinfo(), за исключением того, что эта версия асинхронна.Поведение этой функции можно настроить с помощью
set_custom_hostname_resolver().
await trio.socket.getnameinfo(sockaddr: tuple[str, int] | tuple[str, int, int, int], flags: int) → tuple[str, str]
-
Поиск номера протокола по имени. (Редко используется.)
Аналогично
socket.getprotobyname(), но асинхронно.
await trio.socket.getprotobyname(name: str) → int
Trio намеренно НЕ включает некоторые устаревшие, избыточные или нерабочие функции:
gethostbyname(),gethostbyname_ex(),gethostbyaddr(): устаревшие; используйтеgetaddrinfo()иgetnameinfo()вместо них.-
getservbyport(): устаревшая и содержащая ошибки; вместо этого сделайте:_, service_name = await getnameinfo(('127.0.0.1', port), NI_NUMERICHOST) -
getservbyname(): устаревшая и содержащая ошибки; вместо этого сделайте:await getaddrinfo(None, service_name)
getfqdn(): устаревшая; используйтеgetaddrinfo()со флагомAI_CANONNAME.getdefaulttimeout(),setdefaulttimeout(): вместо этого используйте стандартную поддержку Trio для Отмены и таймаутов.В Windows,
SO_REUSEADDRне экспортируется, потому что это ловушка: имя такое же, как у UnixSO_REUSEADDR, но семантика различна и очень нерабочая. В очень редких случаях, когда вам действительно нуженSO_REUSEADDRв Windows, к нему всё ещё можно получить доступ из модуля стандартной библиотекиsocket.
Объекты сокетов
-
Примечание
trio.socket.SocketType— это абстрактный класс, и его нельзя создавать напрямую; вы получаете конкретные объекты сокетов, вызывая конструкторы, такие какtrio.socket.socket(). Однако вы можете использовать его для проверки, является ли объект сокетом Trio, с помощьюisinstance(obj, trio.socket.SocketType).Объекты сокетов Trio в целом очень похожи на объекты сокетов стандартной библиотеки, но с некоторыми важными отличиями:
Во-первых, и наиболее очевидно, все сделано в стиле Trio: блокирующие методы становятся асинхронными методами, и следующие атрибуты не поддерживаются:
setblocking(): сокеты Trio всегда ведут себя как блокирующие сокеты; если вам нужно читать/писать из нескольких сокетов одновременно, создайте несколько задач.settimeout(): вместо этого см. Отмену и таймауты.makefile(): файловая API Python синхронна, поэтому она не может быть реализована на основе асинхронного сокета.sendall(): Можно было бы поддержать, но лучше использовать более высокоуровневыйSocketStream, а конкретно его методsend_all(), который также выполняет дополнительную проверку ошибок.
Кроме того, следующие методы похожи на аналогичные в
socket.socket, но имеют некоторые особенности, специфичные для Trio:-
Подключает сокет к удалённому адресу.
Аналогично
socket.socket.connect(), но асинхронно.Предупреждение
Из-за ограничений в API операционной системы, иногда невозможно корректно отменить попытку подключения, когда она уже началась. Если
connect()отменён, и не может прервать попытку подключения, то:принудительно закроет сокет, чтобы предотвратить случайное повторное использование
вызовет
Cancelled.
Короче: если
connect()отменён, то сокет остаётся в неизвестном состоянии — возможно, открытым, а возможно, закрытым. Единственный разумный способ — закрыть его.
await connect()-
Проверяет, доступен ли сокет для чтения.
is_readable()sendfile()Мы также отслеживаем дополнительное состояние, так как оно оказывается полезным для
trio.SocketStream:-
Этот атрибут
boolравен True, если вы вызвалиsock.shutdown(SHUT_WR)илиsock.shutdown(SHUT_RDWR), и False в противном случае.
did_shutdown_SHUT_WRСледующие методы идентичны своим аналогам в
socket.socket, но асинхронны, и те, которые принимают аргументы адресов, требуют предварительно разрешённых адресов:recvmsg()(если доступно)recvmsg_into()(если доступно)sendmsg()(если доступно)
Все методы и атрибуты, не упомянутые выше, идентичны своим аналогам в
socket.socket:
class trio.socket.SocketType
Асинхронное ввод-вывод файловой системы
Trio предоставляет встроенные средства для выполнения асинхронных операций с файловой системой, таких как чтение или переименование файла. В целом, мы рекомендуем использовать их вместо обычных синхронных файловых API Python. Но здесь компромиссы несколько тонкие: иногда люди переключаются на асинхронный ввод-вывод, а затем удивляются и теряются, обнаружив, что это не ускоряет их программу. Следующий раздел объясняет теорию асинхронного файлового ввода-вывода, чтобы помочь вам лучше понять поведение вашего кода. Или, если вы просто хотите начать работу, вы можете перейти к обзору API.
Предыстория: почему асинхронный файловый ввод-вывод полезен? Ответ может вас удивить
Многие ожидают, что переключение с синхронного файлового ввода-вывода на асинхронный всегда ускорит их программу. Это не так! Если мы просто посмотрим на общую пропускную способность, то асинхронный файловый ввод-вывод может быть быстрее, медленнее или примерно таким же, и это зависит сложным образом от таких вещей, как ваши точные шаблоны доступа к диску или количество оперативной памяти. Основная мотивация асинхронного файлового ввода-вывода — не повышение пропускной способности, а снижение частоты латентных сбоев.
Чтобы понять почему, вам нужно знать две вещи.
Во-первых, в настоящее время ни одна основная операционная система не предлагает универсальный, надежный, собственный API для асинхронных операций с файлами или файловой системой, поэтому нам приходится имитировать его, используя потоки (в частности, trio.to_thread.run_sync()). Это дешево, но не бесплатно: на типичном ПК отправка в рабочий поток добавляет примерно ~100 мкс накладных расходов на каждую операцию. («мкс» произносится как «микросекунды», и в одной секунде 1 000 000 мкс. Обратите внимание, что все числа здесь являются приблизительными порядками величины, чтобы дать вам представление о масштабе; если вам нужны точные числа для вашей среды, измерьте!)
Во-вторых, стоимость операции с диском невероятно бимодальна. Иногда необходимые данные уже кэшируются в оперативной памяти, и тогда доступ к ним очень, очень быстрый — вызов метода read для кэшированного файла занимает порядка ~1 мкс. Но когда данных нет в кэше, доступ к ним намного медленнее: среднее значение составляет ~100 мкс для твердотельных накопителей и ~10 000 мкс для жестких дисков, а если посмотреть на хвостовые задержки, то для обоих типов хранилищ вы увидите случаи, когда иногда какая-то операция будет в 10 или 100 раз медленнее среднего. И это при условии, что ваша программа — единственное, что пытается использовать этот диск — если вы находитесь на каком-то перегруженном виртуальном сервере в облаке, сражаясь за ввод-вывод с другими арендаторами, то кто знает, что произойдет. И некоторые операции могут потребовать нескольких обращений к диску.
Объединяя эти факты: если данные находятся в оперативной памяти, должно быть ясно, что использование потока — ужасная идея — если вы добавите 100 мкс накладных расходов к операции в 1 мкс, то это замедление в 100 раз! С другой стороны, если ваши данные находятся на жестком диске, то использование потока — это отлично — вместо блокировки основного потока и всех задач на 10 000 мкс, мы блокируем их только на 100 мкс и можем потратить остальное время на выполнение других задач для выполнения полезной работы, что фактически может привести к ускорению в 100 раз.
Но вот проблема: для любой отдельной операции ввода-вывода нет способа заранее узнать, будет ли она одной из быстрых или одной из медленных, поэтому нельзя выбирать и отбирать. При переключении на асинхронный файловый ввод-вывод все быстрые операции замедляются, а все медленные ускоряются. Это выигрыш? С точки зрения общей скорости, трудно сказать: это зависит от того, какие диски вы используете и от того, насколько эффективно ваш ядро кэширует данные диска, что, в свою очередь, зависит от ваших шаблонов доступа к файлам, от того, сколько свободной оперативной памяти у вас есть, от нагрузки на вашу систему… и т.д. Если ответ для вас важен, то нет замены измерению фактического поведения вашего кода в вашей реальной среде развертывания. Но что мы можем сказать, так это то, что асинхронный ввод-вывод с диска делает производительность гораздо более предсказуемой в более широком диапазоне условий выполнения.
Если вы не уверены, что делать, мы рекомендуем использовать асинхронный ввод-вывод с диска по умолчанию, потому что это делает ваш код более устойчивым при плохих условиях, особенно в отношении хвостовых задержек; это повышает вероятность того, что то, что видят ваши пользователи, соответствует тому, что вы видели при тестировании. Блокировка основного потока останавливает все задачи на это время. 10 000 мкс — это 10 мс, и не нужно много 10-миллисекундных сбоев, чтобы начать накапливать реальные деньги; асинхронный ввод-вывод с диска может помочь предотвратить это. Просто не ждите чуда и осознавайте компромиссы.
Обзор API
Если вы хотите выполнять общие операции с файловой системой, такие как создание и перечисление каталогов, переименование файлов или проверку метаданных файлов — или если вы просто хотите удобный способ работы с путями к файлам — вам понадобится trio.Path. Это асинхронизированная замена стандартной библиотеки pathlib.Path и предоставляет тот же полный набор операций.
Для чтения и записи в файлы и файлы-подобные объекты Trio также предоставляет механизм для обертывания любого синхронного файла-подобного объекта в асинхронный интерфейс. Если у вас есть объект trio.Path, вы можете получить один из них, вызвав его метод open(); или если вы знаете имя файла, вы можете открыть его напрямую с помощью trio.open_file(). В качестве альтернативы, если у вас уже есть открытый файл-подобный объект, вы можете обернуть его с помощью trio.wrap_file() — один из случаев, где это особенно полезно, — обернуть io.BytesIO или io.StringIO при написании тестов.
Асинхронные объекты пути
-
Асинхронный объект
pathlib.Path, выполняющий блокирующие методы вtrio.to_thread.run_sync().Создание объекта
Pathвозвращает конкретный подкласс, специфичный для платформы, один изPosixPathилиWindowsPath.-
Аналогично
absolute(), но асинхронно.Возвращает абсолютный путь, добавляя текущий рабочий каталог. Нормализация или разрешение символьных ссылок не выполняется.
Используйте resolve(), чтобы получить канонический путь к файлу.
await absolute()-
Сочетание диска и корня, или ‘’.
property anchor-
Возвращает строковое представление пути с косыми чертами (/).
as_posix()-
Возвращает путь как URI «файла».
as_uri()-
Аналогично
chmod(), но асинхронно.Изменяет разрешения пути, как os.chmod().
await chmod(mode, *, follow_symlinks=True)-
Аналогично
cwd(), но асинхронно.Возвращает новый путь, указывающий на текущий рабочий каталог (как возвращает os.getcwd()).
classmethod await cwd()-
Префикс диска (буква или UNC-путь), если есть.
property drive-
Аналогично
exists(), но асинхронно.Существует ли этот путь.
await exists()-
Аналогично
expanduser(), но асинхронно.Возвращает новый путь с расширенными конструкциями ~ и ~user (как возвращает os.path.expanduser).
await expanduser()-
Аналогично
glob(), но асинхронно.Итерируется по поддереву и возвращает все существующие файлы (любого типа, включая каталоги), соответствующие заданному относительному шаблону.
Это асинхронный метод, который возвращает синхронный итератор, поэтому вы используете его так:
for subpath in await mypath.glob(): ...Примечание
Итератор загружается в память сразу при первом вызове (см. проблему #501 для обсуждения).
await glob(pattern)-
Аналогично
group(), но асинхронно.Возвращает имя группы файла gid.
await group()-
Аналогично
hardlink_to(), но асинхронно.Создаёт жёсткую ссылку на этот путь, указывающую на тот же файл, что и target.
Обратите внимание на порядок аргументов (self, target) — обратный по сравнению с os.link.
await hardlink_to(target)-
Аналогично
home(), но асинхронно.Возвращает новый путь, указывающий на домашний каталог пользователя (как возвращает os.path.expanduser(‘~’)).
classmethod await home()-
True, если путь абсолютный (имеет корень и, применимо, диск).
is_absolute()-
Аналогично
is_block_device(), но асинхронно.Является ли этот путь блочным устройством.
await is_block_device()-
Аналогично
is_char_device(), но асинхронно.Является ли этот путь символьным устройством.
await is_char_device()-
Аналогично
is_dir(), но асинхронно.Является ли этот путь каталогом.
await is_dir()-
Аналогично
is_fifo(), но асинхронно.Является ли этот путь FIFO.
await is_fifo()-
Аналогично
is_file(), но асинхронно.Является ли этот путь обычным файлом (также True для символьных ссылок, указывающих на обычные файлы).
await is_file()-
Аналогично
is_mount(), но асинхронно.Проверяет, является ли этот путь точкой монтирования POSIX.
await is_mount()-
Возвращает True, если путь является относительным к другому пути, или False.
is_relative_to(*other)-
Возвращает True, если путь содержит одно из специальных имён, зарезервированных системой, если таковые имеются.
is_reserved()-
Аналогично
is_socket(), но асинхронно.Является ли этот путь сокетом.
await is_socket()-
Аналогично
is_symlink(), но асинхронно.Является ли этот путь символьной ссылкой.
await is_symlink()-
Аналогично
iterdir(), но асинхронно.Итерируется по файлам в этом каталоге. Не возвращает результаты для специальных путей «.» и «..».
Это асинхронный метод, который возвращает синхронный итератор, поэтому вы используете его так:
for subpath in await mypath.iterdir(): ...Примечание
Итератор загружается в память сразу при первом вызове (см. проблему #501 для обсуждения).
await iterdir()-
Объединяет этот путь с одним или несколькими аргументами и возвращает новый путь, представляющий либо подпуть (если все аргументы являются относительными путями), либо совершенно другой путь (если один из аргументов закреплён).
joinpath(*args)-
Аналогично
lchmod(), но асинхронно.Подобно chmod(), но если путь указывает на символическую ссылку, разрешения символьной ссылки изменяются, а не её цели.
await lchmod(mode)-
Аналогично
link_to(), но асинхронно.Создаёт жёсткую ссылку на целевой путь, указывающую на этот путь.
Обратите внимание, что эта функция не создаёт жёсткую ссылку на этот путь, указывающую на target, несмотря на подразумеваемое действие функции и названия аргументов. Порядок аргументов (target, link) обратный по сравнению с Path.symlink_to, но соответствует порядку в os.link.
Устарело начиная с Python 3.10 и планируется к удалению в Python 3.12. Используйте
hardlink_to()вместо этого.
await link_to(target) -
class trio.Path(*args: str | os.PathLike[str])
-
Как
lstat(), но асинхронно.Аналогично stat(), но если путь указывает на символическую ссылку, возвращаются данные о статусе самой ссылки, а не её целевого объекта.
await lstat()-
Возвращает True, если этот путь соответствует заданному шаблону.
match(path_pattern)-
Как
mkdir(), но асинхронно.Создаёт новую директорию по заданному пути.
await mkdir(mode=511, parents=False, exist_ok=False)-
Конечный компонент пути, если он есть.
property name-
Как
open(), но асинхронно.Открывает файл, указанный этим путём, и возвращает объект файла, как и встроенная функция open().
await open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)-
Как
owner(), но асинхронно.Возвращает имя пользователя владельца файла.
await owner()-
Логический родительский элемент пути.
property parent-
Последовательность логических родительских элементов этого пути.
property parents-
Объект, обеспечивающий последовательный доступ к компонентам пути в файловой системе.
property parts-
Как
read_bytes(), но асинхронно.Открывает файл в двоичном режиме, считывает его и закрывает.
await read_bytes()-
Как
read_text(), но асинхронно.Открывает файл в текстовом режиме, считывает его и закрывает.
await read_text(encoding=None, errors=None)-
Как
readlink(), но асинхронно.Возвращает путь, на который указывает символическая ссылка.
await readlink()-
Возвращает относительный путь к другому пути, определённому переданными аргументами. Если операция невозможна (потому что это не подпуть другого пути), генерирует исключение ValueError.
relative_to(*other)-
Как
rename(), но асинхронно.Переименовывает этот путь в целевой путь.
Целевой путь может быть абсолютным или относительным. Относительные пути интерпретируются относительно текущей рабочей директории, а не директории объекта Path.
Возвращает новый объект Path, указывающий на целевой путь.
await rename(target)-
Как
replace(), но асинхронно.Переименовывает этот путь в целевой путь, перезаписывая, если целевой путь существует.
Целевой путь может быть абсолютным или относительным. Относительные пути интерпретируются относительно текущей рабочей директории, а не директории объекта Path.
Возвращает новый объект Path, указывающий на целевой путь.
await replace(target)-
Как
resolve(), но асинхронно.Делает путь абсолютным, разрешает все символические ссылки по пути и также нормализует его.
await resolve(strict=False)-
Как
rglob(), но асинхронно.Рекурсивно возвращает все существующие файлы (любого типа, включая директории), соответствующие заданному относительному шаблону, где-либо в этом поддереве.
Это асинхронный метод, возвращающий синхронный итератор, поэтому вы используете его так:
for subpath in await mypath.rglob(): ...Примечание
Итератор загружается в память сразу при первом вызове (см. issue #501 для обсуждения).
await rglob(pattern)-
Как
rmdir(), но асинхронно.Удаляет эту директорию. Директория должна быть пустой.
await rmdir()-
Корень пути, если он есть.
property root-
Как
samefile(), но асинхронно.Возвращает, является ли other_path тем же файлом, что и этот (как возвращает os.path.samefile()).
await samefile(other_path)-
Как
stat(), но асинхронно.Возвращает результат системного вызова stat() для этого пути, как делает os.stat().
await stat(*, follow_symlinks=True)-
Конечный компонент пути без последнего суффикса.
property stem-
Последний суффикс конечного компонента, если он есть.
Включает ведущую точку. Например: ‘.txt’
property suffix-
Список суффиксов конечного компонента, если они есть.
Включают ведущие точки. Например: [‘.tar’, ‘.gz’]
property suffixes-
Как
symlink_to(), но асинхронно.Создаёт символическую ссылку на этот путь, указывающую на целевой путь. Обратите внимание, что порядок аргументов (ссылка, цель) обратный по сравнению с os.symlink.
await symlink_to(target, target_is_directory=False)-
Как
touch(), но асинхронно.Создаёт этот файл с заданным режимом доступа, если он не существует.
await touch(mode=438, exist_ok=True)-
Как
unlink(), но асинхронно.Удаляет этот файл или ссылку. Если путь указывает на директорию, используйте rmdir() вместо этого.
await unlink(missing_ok=False)-
Возвращает новый путь с изменённым именем файла.
with_name(name)-
Возвращает новый путь с изменённым значением stem.
with_stem(stem)-
Возвращает новый путь с изменённым суффиксом файла. Если у пути нет суффикса, добавляет указанный суффикс. Если указанный суффикс пустая строка, удаляет суффикс из пути.
with_suffix(suffix)-
Как
write_bytes(), но асинхронно.Открывает файл в двоичном режиме, записывает в него и закрывает.
await write_bytes(data)-
Как
write_text(), но асинхронно.Открывает файл в текстовом режиме, записывает в него и закрывает.
await write_text(data, encoding=None, errors=None, newline=None)-
-
Асинхронный
pathlib.PosixPath, который выполняет блокирующие методы вtrio.to_thread.run_sync().
class trio.PosixPath(*args: str | os.PathLike[str])
-
Асинхронный
pathlib.WindowsPath, который выполняет блокирующие методы вtrio.to_thread.run_sync().
class trio.WindowsPath(*args: str | os.PathLike[str])
Асинхронные объекты файлов
-
Асинхронная версия
open().-
Объект асинхронного файла
Возвращает:
Пример:
async with await trio.open_file(filename) as f: async for line in f: pass assert f.closedСм. также
-
await trio.open_file(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=None, opener=None)
-
Оборачивает любой объект файла в обёртку, предоставляющую интерфейс асинхронного объекта файла.
-
file – объект файла
-
Объект асинхронного файла, который оборачивает
file
Параметры:
Возвращает:
Пример:
async_file = trio.wrap_file(StringIO('asdf')) assert await async_file.read() == 'asdf' -
trio.wrap_file(file)
-
Асинхронные объекты файлов Trio имеют интерфейс, который автоматически адаптируется к объекту, который оборачивается. Интуитивно, вы можете в основном рассматривать их как обычный объект файла, за исключением добавления
awaitперед любыми методами, выполняющими ввод-вывод. Определение объекта файла в Python немного неясно, поэтому вот детали:Синхронные атрибуты/методы: если присутствуют следующие атрибуты или методы, они переэкспортируются без изменений:
closed,encoding,errors,newlines,isatty,readable,seekable,writable,buffer,raw,line_buffering,closefd,name,mode,getvalue,getbuffer.Асинхронные методы: если присутствуют следующие методы, они переэкспортируются как асинхронные методы:
flush,read,read1,readall,readinto,readline,readlines,seek,tell,truncate,write,writelines,readinto1,peek,detach.
Особые заметки:
Объекты асинхронных файлов реализуют интерфейс Trio
AsyncResource: их закрывают, вызываяaclose()вместоclose(!!), и они могут использоваться как асинхронные контекстные менеджеры. Как и все методыaclose(), методacloseдля асинхронных объектов файлов гарантированно закрывает файл перед возвратом, даже если он отменён или по какой-либо причине вызывает ошибку.Использование одного и того же асинхронного объекта файла из нескольких задач одновременно: поскольку асинхронные методы асинхронных объектов файлов реализуются с помощью потоков, безопасно вызывать два из них одновременно из разных задач ТОЛЬКО если лежащий в основе синхронный объект файла является потокобезопасным. Вы должны обратиться к документации для объекта, который вы оборачиваете. Для объектов, возвращаемых из
trio.open_file()илиtrio.Path.open(), это зависит от того, открываете ли вы файл в двоичном или текстовом режиме: файлы в двоичном режиме безопасны для задач/потоков, файлы в текстовом режиме — нет.-
Асинхронные объекты файлов могут использоваться как асинхронные итераторы для перебора строк файла:
async with await trio.open_file(...) as f: async for line in f: print(line) Метод
detach, если он присутствует, возвращает асинхронный объект файла.
Это должно включать все атрибуты, экспонируемые классами в
io. Но если вы оборачиваете объект, у которого есть другие атрибуты, которых нет в этом списке, вы можете получить к ним доступ через атрибут.wrapped:-
Основной синхронный объект файла.
wrapped
Asynchronous file interface
Запуск дочерних процессов
Trio предоставляет поддержку запуска других программ как дочерних процессов, взаимодействие с ними через каналы, отправку сигналов и ожидание их завершения.
В большинстве случаев это делается через наш интерфейс высокого уровня, trio.run_process. Он позволяет либо запустить процесс до завершения, при этом необязательно захватив вывод, либо запустить его в фоновом задании и взаимодействовать с ним во время его выполнения:
-
Запустить
commandв дочернем процессе и дождаться его завершения.Эта функция может быть вызвана двумя способами.
Один вариант — прямой вызов, например:
completed_process_info = await trio.run_process(...)
В этом случае она возвращает экземпляр
subprocess.CompletedProcess, описывающий результаты. Используйте это, если хотите обращаться с процессом как с вызовом функции.Другой вариант — запустить его как задачу с помощью
Nursery.start— улучшенной версииstart_soon, которая позволяет задаче возвращать значение во время запуска:process = await nursery.start(trio.run_process, ...)
В этом случае
startвозвращает объектProcess, который можно использовать для взаимодействия с процессом во время его работы. Используйте это, если хотите обращаться с процессом как с фоновой задачей.В любом случае,
run_processгарантирует, что процесс завершится до возврата, обрабатывает отмену, необязательно проверяет ошибки и предоставляет некоторые удобные сокращения для работы со вводом/выводом дочернего процесса.Входные данные:
run_processподдерживает все те жеstdin=аргументы, что иsubprocess.Popen. Кроме того, если вы просто хотите передать некоторые фиксированные данные, вы можете передать обычный объектbytes, иrun_processпозаботится о настройке канала, подаче переданных данных и отправке конца файла. По умолчанию этоb"", что означает, что дочерний процесс получит пустой stdin. Если вы хотите, чтобы дочерний процесс читал со стандартного ввода родительского процесса, используйтеstdin=None.Вывод: По умолчанию любой вывод, созданный дочерним процессом, передаётся в стандартные потоки вывода и ошибок родительского процесса Trio.
При прямом вызове
run_processвы можете захватить вывод дочернего процесса, передавcapture_stdout=Trueдля захвата стандартного вывода дочернего процесса и/илиcapture_stderr=Trueдля захвата его стандартной ошибки. Захваченные данные собираются Trio в буфер оперативной памяти, а затем предоставляются как атрибутыstdoutи/илиstderrвозвращенного объектаCompletedProcess. Значение для любого потока, который не был захвачен, будетNone.Если вы хотите захватить как stdout, так и stderr, сохраняя их раздельно, передайте
capture_stdout=True, capture_stderr=True.Если вы хотите захватить как stdout, так и stderr, но перемешать их в порядке их вывода, используйте:
capture_stdout=True, stderr=subprocess.STDOUT. Это перенаправляет stderr дочернего процесса в его stdout, поэтому объединенный вывод будет доступен в атрибутеstdout.Если вы используете
await nursery.start(trio.run_process, ...)и хотите захватить вывод дочернего процесса для дальнейшей обработки, используйтеstdout=subprocess.PIPE, а затем убедитесь, что вы прочитали данные из потокаProcess.stdout. Если вы хотите захватить stderr отдельно, используйтеstderr=subprocess.PIPE. Если вы хотите захватить оба потока, но перемешанными в правильном порядке, используйтеstdout=subprocess.PIPE, stderr=subprocess.STDOUT.Проверка ошибок: Если дочерний процесс завершается с ненулевым кодом состояния, что указывает на ошибку,
run_process()генерирует исключениеsubprocess.CalledProcessErrorвместо нормального возврата. Захваченные выводы по-прежнему доступны как атрибутыstdoutиstderrэтого исключения. Чтобы отключить это поведение, чтобыrun_process()возвращался нормально, даже если дочерний процесс завершился аномально, передайтеcheck=False.Обратите внимание, что это может сделать аргументы
capture_stdoutиcapture_stderrполезными даже при запускеrun_processкак задачи: если вам нужен вывод только в случае сбоя процесса, вы можете включить захват и затем прочитать вывод изCalledProcessError.Отмена: При отмене
run_processотправляет запрос на завершение дочернему процессу, а затем ждёт его полного завершения. Аргументdeliver_cancelпозволяет управлять способом завершения процесса.Примечание
run_processпреднамеренно аналогичен стандартной библиотекеsubprocess.run, но некоторые значения по умолчанию отличаются. В частности, мы используем:check=True, потому что “ошибки никогда не должны проходить молча / если только они не будут явно умолкнуты”.stdin=b"", потому что это даёт менее запутанные результаты, если дочерний процесс неожиданно пытается прочитать со stdin.
Чтобы получить семантику
subprocess.run, используйтеcheck=False, stdin=None.
await trio.run_process(command: str | bytes | os.PathLike | Sequence[str | bytes | os.PathLike], *, stdin: bytes | bytearray | memoryview | int | HasFileno | None = b'', capture_stdout: bool = False, capture_stderr: bool = False, check: bool = True, deliver_cancel: Callable[[Process], Awaitable[object]] | None = None, task_status: TaskStatus[Process] = TASK_STATUS_IGNORED, **options: object) → subprocess.CompletedProcess[bytes]
-
command (список или строка) – Команда для выполнения. Обычно это последовательность строк, например,
['ls', '-l', 'directory with spaces'], где первый элемент указывает исполняемый файл, а остальные — его аргументы. В**optionsили на Windows,commandможет быть строкой, которая будет обработана в соответствии с платформенно-зависимыми правилами цитирования.-
stdin (
bytes, subprocess.PIPE, дескриптор файла или None) –байты, которые нужно передать подпроцессу в стандартный поток ввода или
None, если стандартный поток ввода подпроцесса должен поступать оттуда же, откуда стандартный поток ввода родительского процесса Trio. Как и в случае с модулемsubprocess, вы также можете передать дескриптор файла или объект с методомfileno(), в этом случае стандартный поток ввода подпроцесса будет поступать из этого файла.При запуске
run_processкак фоновой задачи, вы также можете использоватьstdin=subprocess.PIPE, в этом случаеProcess.stdinбудетSendStream, который вы можете использовать для отправки данных в дочерний процесс. capture_stdout (bool) – Если True, то захватывает байты, которые подпроцесс записывает в стандартный поток вывода, и возвращает их в атрибуте
stdoutвозвращаемого объектаsubprocess.CompletedProcessилиsubprocess.CalledProcessError.capture_stderr (bool) – Если True, то захватывает байты, которые подпроцесс записывает в стандартный поток ошибок, и возвращает их в атрибуте
stderrвозвращаемого объектаCompletedProcessилиsubprocess.CalledProcessError.check (bool) – Если False, то не проверяет, завершился ли подпроцесс успешно. Вы должны убедиться в проверке атрибута
returncodeвозвращаемого объекта, если вы передаетеcheck=False, чтобы ошибки не пропускались незамеченными.-
deliver_cancel (асинхронная функция или None) –
Если
run_processотменено, то необходимо убить дочерний процесс. Существует несколько способов сделать это, поэтому мы позволяем вам настроить его.Если вы передаете None (по умолчанию), то поведение зависит от платформы:
В Windows Trio вызывает
TerminateProcess, которая должна немедленно убить процесс.В Unix-подобных системах по умолчанию происходит отправка
SIGTERM, ожидание 5 секунд и отправкаSIGKILL.
В качестве альтернативы можно настроить это поведение, передав произвольную асинхронную функцию, которая будет вызвана с объектом
Processв качестве аргумента. Например, стандартное поведение в Unix можно реализовать так:async def my_deliver_cancel(process): process.send_signal(signal.SIGTERM) await trio.sleep(5) process.send_signal(signal.SIGKILL)Когда процесс фактически завершается, функция
deliver_cancelавтоматически отменяется — поэтому, если процесс завершается послеSIGTERM, мы никогда не достигнемSIGKILL.В любом случае
run_processвсегда будет ждать завершения дочернего процесса перед поднятиемCancelled. **options –
run_process()также принимает любые общие параметры подпроцесса и передает их конструкторуProcess. Это включает параметрыstdoutиstderr, которые предоставляют дополнительные возможности перенаправления, такие какstderr=subprocess.STDOUT,stdout=subprocess.DEVNULLили дескрипторы файлов.
-
При обычном вызове — экземпляр
subprocess.CompletedProcess, описывающий код возврата и выходные данные.При вызове через
Nursery.start— экземплярtrio.Process. -
UnicodeError – если
stdinуказано как строка Unicode, а не байтыValueError – если для одного и того же потока указано несколько перенаправлений, например, и
capture_stdout=True, иstdout=subprocess.DEVNULLsubprocess.CalledProcessError – если
check=Falseне передано и процесс завершился с ненулевым кодом возвратаOSError – если при запуске или взаимодействии с процессом произошла ошибка
ExceptionGroup – если в
deliver_cancelпроизошли исключения или когда при взаимодействии с подпроцессом произошли исключения. Если strict_exception_groups установлено в значение False в глобальном контексте, что устарело, то отдельные исключения будут объединены.
Параметры:
Возвращаемое значение:
Исключения:
Примечание
Дочерний процесс выполняется в той же группе процессов, что и родительский процесс Trio, поэтому Ctrl+C одновременно передается как родительскому, так и дочернему процессу. Если вы не хотите этого поведения, обратитесь к документации вашей платформы по запуску дочерних процессов в другой группе процессов.
-
-
Представляет любой файлоподобный объект, имеющий дескриптор файла.
fileno() → int
class trio._subprocess.HasFileno(Protocol)
-
Поток-дочерний процесс. Подобно
subprocess.Popen, но асинхронный.У этого класса нет публичного конструктора. Наиболее распространённый способ получить объект
Process— это объединитьNursery.startсrun_process:process_object = await nursery.start(run_process, ...)
Таким образом,
run_processконтролирует процесс и гарантирует его надлежащее завершение, а также (необязательно) проверяет возвращаемое значение, подаёт на вход данные и т. д.Если вам нужен больший контроль — например, потому что вы хотите запустить дочерний процесс, который переживёт вашу программу — то ещё одним вариантом является использование
trio.lowlevel.open_process:process_object = await trio.lowlevel.open_process(...)
-
command, переданный при создании, определяющий исполняемый процесс и его аргументы.
args-
Идентификатор процесса (PID) управляемого этим объектом дочернего процесса.
Тип:
pid-
Поток, подключенный к стандартному потоку ввода дочернего процесса: при записи байтов в него, они становятся доступными для чтения дочерним процессом. Доступен только если
Processбыл создан с использованиемstdin=PIPE; в противном случае будет None.-
trio.abc.SendStream или None
Тип:
-
stdin-
Поток, подключенный к стандартному потоку вывода дочернего процесса: когда дочерний процесс записывает в стандартный вывод, записанные байты становятся доступны для чтения здесь. Доступен только если
Processбыл создан с использованиемstdout=PIPE; в противном случае будет None.-
trio.abc.ReceiveStream или None
Тип:
-
stdout-
Поток, подключенный к стандартному потоку ошибок дочернего процесса: когда дочерний процесс записывает в стандартный вывод ошибок, записанные байты становятся доступны для чтения здесь. Доступен только если
Processбыл создан с использованиемstderr=PIPE; в противном случае будет None.-
trio.abc.ReceiveStream или None
Тип:
-
stderr-
Поток, отправляющий данные в стандартный поток ввода дочернего процесса и получающий их из стандартного потока вывода. Доступен только если оба
stdinиstdoutдоступны; в противном случае будет None.-
trio.StapledStream или None
Тип:
-
stdio-
Код завершения процесса (целое число) или
None, если он всё ещё выполняется.По соглашению, код возврата 0 означает успех. В системах UNIX отрицательные значения указывают на завершение из-за сигнала, например, -11 при завершении сигналом 11 (
SIGSEGV). В Windows, процесс, завершившийся из-за вызоваProcess.terminate(), будет иметь код завершения 1.В отличие от стандартной библиотеки
subprocess.Popen.returncode, вам не нужно вызыватьpollилиwaitдля обновления этого атрибута; он автоматически обновляется по мере необходимости и всегда предоставляет последнюю информацию.
returncode-
Ожидать завершения процесса.
-
Код завершения процесса; см.
returncode.
Возвращает:
-
await wait() → int-
Возвращает код завершения процесса (целое число) или
None, если он всё ещё выполняется.Обратите внимание, что в Trio (в отличие от стандартной библиотеки
subprocess.Popen),process.poll()иprocess.returncodeвсегда возвращают один и тот же результат. См.returncodeдля получения более подробной информации. Этот метод включён только для облегчения переноса кода изsubprocess.
poll() → int | None-
Немедленно завершить процесс.
В системах UNIX это эквивалентно
send_signal(signal.SIGKILL). В Windows вызываетсяTerminateProcess. В обоих случаях процесс не может предотвратить своё убийство, но завершение будет доставлено асинхронно; используйтеwait(), если вы хотите убедиться, что процесс действительно завершён, прежде чем продолжать.
kill() → None-
Завершить процесс вежливо, если это возможно.
В системах UNIX это эквивалентно
send_signal(signal.SIGTERM); по соглашению это запрос на вежливое завершение, но плохо написанный или неисправный процесс может его проигнорировать. В Windowsterminate()принудительно завершает процесс так же, какkill().
terminate() → None-
Отправить сигнал
sigпроцессу.В системах UNIX,
sigможет быть любым сигналом, определённым в модулеsignal, например,signal.SIGINTилиsignal.SIGTERM. В Windows это может быть всё, что принимает стандартная библиотекаsubprocess.Popen.send_signal().
send_signal(sig: signal.Signals | int) → NoneПримечание
communicate()не предоставляется как метод для объектовProcess; вызывайтеrun_process()обычно для простого захвата или напишите цикл сами, если у вас есть особые потребности.communicate()имеет довольно необычное поведение отмены в стандартной библиотеке (в некоторых системах он запускает фоновый поток, который продолжает читать из дочернего процесса даже после истечения срока ожидания), и мы хотели предоставить интерфейс с меньшим количеством неожиданностей. -
class trio.Process
Если trio.run_process слишком ограничивает, мы также предлагаем низкоуровневый API, trio.lowlevel.open_process. Например, если вы хотите запустить дочерний процесс, который переживёт родительский процесс и станет сиротой, то run_process этого сделать не может, но open_process может.
Параметры запуска дочерних процессов
Все API Trio для работы с дочерними процессами принимают множество ключевых аргументов, используемых стандартным модулем subprocess для управления средой запуска процесса и механизмами коммуникации с ним. Эти параметры могут быть переданы там, где в документации ниже вы видите **options. Полный список см. здесь, а часто используемые — здесь в документации subprocess. (Возможно, вам потребуется import
subprocess для доступа к константам, таким как PIPE или DEVNULL.)
В настоящее время Trio всегда использует небуферизованные потоки байтов для общения с процессом, поэтому он не поддерживает параметры encoding, errors, universal_newlines (псевдоним text) и bufsize.
Цитата: больше, чем вы хотели знать
Команда для запуска и её аргументы обычно должны передаваться API Trio для работы с подпроцессами как последовательность строк, где первый элемент последовательности задаёт команду для запуска, а оставшиеся элементы — её аргументы, по одному аргументу на элемент. Эта форма используется, чтобы избежать потенциальных проблем с цитированием; например, вы можете запустить ["cp", "-f", source_file, dest_file], не беспокоясь о том, содержит ли source_file или dest_file пробелы.
Если вы запускаете подпроцессы без shell=True и на UNIX-системах, то это всё, что вам нужно знать о задании команды. Если вы используете shell=True или работаете на Windows, вам, вероятно, стоит прочитать остальную часть этого раздела, чтобы быть осведомлённым о потенциальных проблемах.
При использовании shell=True на UNIX, вы должны указать команду как одну строку, которая будет передана оболочке так, как будто вы ввели её в интерактивную командную строку. Преимущество этого варианта заключается в том, что он позволяет использовать возможности оболочки, такие как конвейеры и перенаправление, без написания кода для их обработки. Например, вы можете написать Process("ls | grep
some_string", shell=True). Недостаток заключается в том, что вам необходимо учитывать правила цитирования оболочки, обычно заключая в shlex.quote() любой аргумент, который может содержать пробелы, кавычки или другие метасимволы оболочки. Если вы этого не сделаете, ваш, казалось бы, безопасный f"ls | grep {some_string}" может привести к катастрофе при вызове с some_string = "foo; rm -rf /".
В Windows основной API для запуска процессов (система вызовов CreateProcess()) принимает строку, а не список, и на самом деле дочернему процессу решать, как он хочет разбить эту строку на отдельные аргументы. Поскольку язык C определяет, что main() должен принимать список аргументов, большинство программ, с которыми вы сталкиваетесь, будут следовать правилам, используемым Microsoft C/C++ runtime. subprocess.Popen, и, следовательно, Trio, используют эти правила при преобразовании последовательности аргументов в строку, и они документированы вместе с модулем subprocess. Нет документированной функции стандартной библиотеки Python, которая может напрямую выполнить это преобразование, поэтому даже в Windows вы почти всегда хотите передавать последовательность аргументов, а не строку. Но если программа, которую вы запускаете, не разбивает свою командную строку обратно на отдельные аргументы стандартным способом, вам может потребоваться передать строку, чтобы обойти это. (Или вам просто не повезёт: насколько я могу судить, просто нет способа передать аргумент, содержащий двойную кавычку, в пакетный файл Windows.)
В Windows с shell=True всё становится ещё более хаотичным. Теперь применяются два отдельных набора правил цитирования: один — оболочкой командной строки Windows CMD.EXE, а другой — запускаемым процессом, и они разные. (И нет shlex.quote(), чтобы спасти вас: она использует правила цитирования для UNIX, даже в Windows.) Большинство специальных символов, интерпретируемых оболочкой &<>()^|, не обрабатываются как специальные, если оболочка считает их находящимися внутри двойных кавычек, но подстановки переменных среды %FOO% по-прежнему выполняются, и оболочка не предоставляет способ написать двойную кавычку внутри двойных кавычек. За пределами двойных кавычек любой символ (включая двойную кавычку) можно экранировать, добавив ведущий ^. Но поскольку конвейер обрабатывается запуском каждой команды конвейера в подоболочке, может потребоваться несколько уровней экранирования:
echo ^^^&x | find "x" | find "x" # prints: &x
И если вы объедините конвейеры с группировкой (), вам может понадобиться ещё больше уровней экранирования:
(echo ^^^^^^^&x | find "x") | find "x" # prints: &x
Поскольку создание процесса принимает одну строку аргументов, цитирование CMD.EXE не влияет на разбиение слов, а двойные кавычки не удаляются во время прохода расширения CMD.EXE. Двойные кавычки проблематичны, поскольку CMD.EXE обрабатывает их иначе, чем правила MSVC runtime; в:
prog.exe "foo \"bar\" baz"
программа увидит один аргумент foo "bar" baz, но CMD.EXE считает, что bar\ не заключён в кавычки, в то время как foo \ и baz заключены. Всё это делает надёжную интерполяцию чего-либо в командную строку shell=True в Windows сложной задачей, и Trio использует поведение subprocess: если вы передаёте последовательность с shell=True, она цитируется так же, как последовательность с shell=False, и лучше не содержать никаких метасимволов оболочки, которые вы не планировали.
Дополнительные материалы:
Сигналы
-
Менеджер контекста для перехвата сигналов.
Вхождение в этот менеджер контекста начинает прослушивание заданных сигналов и возвращает асинхронный итератор; выход из менеджера контекста останавливает прослушивание.
Асинхронный итератор блокируется до получения сигнала, а затем возвращает его.
Обратите внимание, что если вы выйдете из блока
with, а итератор ещё не обработал ожидающие сигналы, они будут повторно доставлены с помощью стандартной обработки сигналов Python. Это предотвращает гонку при получении сигнала непосредственно перед выходом из блокаwith.-
signals — сигналы для прослушивания.
-
TypeError — если сигналы не были предоставлены.
RuntimeError — если вы пытаетесь использовать это в другом потоке, кроме основного потока Python. (Это ограничение Python.)
Параметры:
Возможные исключения:
Пример
Общей практикой для демонов Unix является перезагрузка конфигурации при получении сигнала
SIGHUP. Вот пример того, как это можно сделать с помощьюopen_signal_receiver():with trio.open_signal_receiver(signal.SIGHUP) as signal_aiter: async for signum in signal_aiter: assert signum == signal.SIGHUP reload_configuration() -
with trio.open_signal_receiver(*signals: signal.Signals | int) → Generator[AsyncIterator[int], None, None] as signal_aiter
© 2017 Nathaniel J. Smith
Licensed under the MIT License.
https://trio.readthedocs.io/en/v0.29.0/reference-io.html