Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 3156 – 비동기 IO 지원의 부활: “asyncio” 모듈

Author:
Guido van Rossum <guido at python.org>
BDFL-Delegate:
Antoine Pitrou <antoine at python.org>
Discussions-To:
python-tulip@googlegroups.com
Status:
Final
Type:
Standards Track
Created:
12-Dec-2012
Python-Version:
3.3
Post-History:
21-Dec-2012
Replaces:
3153
Resolution:
Python-Dev message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

Python 3.3부터 시작하는 Python 3의 비동기 I/O를 위한 제안입니다. 이것을 PEP 3153에서 빠져 있던 구체적인 제안으로 간주하십시오. 이 제안에는 Twisted의 것과 유사한 플러그형 이벤트 루프, 전송 및 프로토콜 추상화와 yield from을 기반으로 하는 고수준 스케줄러가 포함됩니다 (PEP 380). 제안된 패키지 이름은 asyncio입니다.

소개

상태

Tulip이라는 코드명으로 참조 구현이 존재합니다. Tulip 저장소는 마지막의 참고 문헌 섹션에 연결되어 있습니다. 이 저장소를 기반으로 한 패키지는 Python 3.3 설치 환경에서 asyncio 패키지를 사용할 수 있도록 PyPI에서 제공됩니다(참고 문헌 참조).

2013년 10월 20일 현재, asyncio 패키지는 Python 3.4 저장소에 추가되었으며 “provisional” API 상태로 Python 3.4-alpha-4와 함께 릴리스되었습니다. 이는 신뢰를 표현하고 API에 대한 초기 피드백을 늘리기 위한 것이며, PEP의 수용을 강제하기 위한 것이 아닙니다. 이 패키지는 Python 3.4에서 잠정 상태를 유지하고 Python 3.5에서 최종 상태로 발전할 것으로 예상됩니다. 개발은 주로 Tulip 저장소에서 계속 진행되며, 변경 사항은 때때로 CPython 저장소에 병합됩니다.

종속성

제안된 기능 중 상당수에는 Python 3.3이 필요합니다. 참조 구현(Tulip)은 Python 3.3을 넘어서는 새로운 언어 또는 표준 라이브러리 기능, 서드파티 모듈이나 패키지, C 코드를 필요로 하지 않으며, Windows에서의 (선택적) IOCP 지원은 예외입니다.

모듈 네임스페이스

이 명세는 새로운 최상위 패키지인 asyncio에 있습니다. 서로 다른 구성 요소는 이 패키지의 별도 서브모듈에 있습니다. 이 패키지는 각 해당 서브모듈에서 공통 API를 가져와 패키지 속성으로 사용할 수 있게 합니다(email 패키지의 작동 방식과 유사합니다). 이러한 공통 API의 경우 실제로 해당 API를 정의하는 서브모듈의 이름은 명세의 일부가 아닙니다. 덜 일반적인 API는 해당 서브모듈에서 명시적으로 가져와야 할 수 있으며, 이 경우 서브모듈 이름은 명세의 일부입니다.

서브모듈 이름 없이 정의된 클래스와 함수는 최상위 패키지의 네임스페이스에 있는 것으로 간주합니다. (단, 간결성을 위해 특정 상황에서 네임스페이스 접두사 없이도 사용되는 여러 클래스의 메서드와 이를 혼동하지 마십시오.)

상호 운용성

이벤트 루프는 대부분의 상호 운용이 이루어지는 곳입니다. Twisted, Tornado 또는 gevents와 같은 프레임워크(Python 3.3 포트)가 경량 어댑터나 프록시를 사용하여 기본 이벤트 루프 구현을 필요에 맞게 조정하거나, 자체 이벤트 루프 구현을 조정한 것으로 기본 이벤트 루프 구현을 대체하기가 쉬워야 합니다. (Twisted와 같은 일부 프레임워크는 여러 이벤트 루프 구현을 갖고 있습니다. 이러한 구현은 모두 동일한 인터페이스를 가지므로 문제가 되지 않습니다.)

대부분의 경우 서로 다른 두 서드파티 프레임워크는 기본 이벤트 루프 구현을 공유하거나(각자 자체 어댑터 사용), 어느 한 프레임워크의 이벤트 루프 구현을 공유하여 상호 운용할 수 있어야 합니다. 후자의 경우 두 단계의 조정이 발생합니다(프레임워크 A의 이벤트 루프에서 표준 이벤트 루프 인터페이스로, 그리고 거기서 프레임워크 B의 이벤트 루프로). 어떤 이벤트 루프 구현을 사용할지는 주 프로그램이 제어해야 합니다(이벤트 루프 선택을 위한 기본 정책은 제공됩니다).

이러한 상호 운용성이 효과적으로 이루어지려면, 서드파티 프레임워크에서 선호되는 적응 방향은 기본 이벤트 루프를 유지하고 이를 프레임워크의 API에 맞게 조정하는 것입니다. 이상적으로는 모든 서드파티 프레임워크가 자체 이벤트 루프 구현을 포기하고 표준 구현을 사용해야 합니다. 그러나 모든 프레임워크가 표준 구현에서 제공하는 기능에 만족하는 것은 아닐 수 있습니다.

두 가지 적응 방향을 모두 지원하기 위해 두 개의 별도 API를 지정합니다.

  • 현재 이벤트 루프를 관리하기 위한 인터페이스
  • 규격을 준수하는 이벤트 루프의 인터페이스

이벤트 루프 구현은 추가 메서드와 보장을 제공할 수 있으며, 이러한 항목은 문서에서 비표준 항목이라고 명시해야 합니다. 이벤트 루프 구현은 주어진 환경에서 구현할 수 없는 경우 특정 메서드를 구현하지 않은 상태로 둘 수도 있습니다. 그러나 표준 API에서 벗어나는 이러한 동작은 최후의 수단으로만, 그리고 플랫폼이나 환경이 이를 강제하는 경우에만 고려해야 합니다. (예를 들어 시작하거나 중지할 수 없는 시스템 이벤트 루프가 있는 플랫폼을 들 수 있습니다. 아래의 “임베디드 이벤트 루프”를 참조하십시오.)

이벤트 루프 API는 await/yield from에 의존하지 않습니다. 대신 콜백, 추가 인터페이스(트랜스포트와 프로토콜), 그리고 Future를 조합하여 사용합니다. 후자는 PEP 3148에서 정의된 것과 유사하지만 구현이 다르며 스레드에 종속되지 않습니다. 특히 결과가 아직 준비되지 않은 경우 result() 메서드는 블로킹하는 대신 예외를 발생시킵니다. 사용자는 콜백(또는 await/yield from)을 사용하여 결과를 기다려야 합니다.

코루틴을 반환하는 것으로 지정된 모든 이벤트 루프 메서드는 구현 방식에 따라 Future 또는 코루틴 중 하나를 반환할 수 있습니다(표준 구현은 항상 코루틴을 반환합니다). 코루틴 인자를 받는 것으로 문서화된 모든 이벤트 루프 메서드는 해당 인자에 대해 Future와 코루틴을 모두 받아야 합니다. (코루틴 또는 Future인 인자를 Future로 변환하는 편의 함수인 ensure_future()가 있습니다.)

콜백 사용을 좋아하지 않는 사용자(저와 같은)를 위해 PEP 380의 yield from 또는 PEP 492await 표현식을 사용하여 비동기 I/O 코드를 코루틴으로 작성할 수 있도록 스케줄러를 제공합니다. 스케줄러는 플러그형이 아닙니다. 플러그 가능성은 이벤트 루프 수준에서 제공되며, 표준 스케줄러 구현은 규격을 준수하는 모든 이벤트 루프 구현에서 작동해야 합니다. (실제로 이는 규격 준수 구현을 판별하는 중요한 리트머스 시험입니다.)

코루틴을 사용하여 작성된 코드와 다른 비동기 프레임워크 간의 상호 운용성을 위해 스케줄러는 Future처럼 동작하는 Task 클래스를 정의합니다. 이벤트 루프 수준에서 상호 운용되는 프레임워크는 Future에 콜백을 추가하여 Future가 완료될 때까지 기다릴 수 있습니다. 마찬가지로 스케줄러는 콜백이 호출될 때까지 코루틴을 일시 중단하는 연산을 제공합니다.

이러한 프레임워크가 Future 및 Task 클래스를 있는 그대로 사용할 수 없다면 loop.create_future()loop.create_task() 메서드를 다시 구현할 수 있습니다. 이 메서드들은 Future/Task 인터페이스를 구현하는 객체(또는 그 상위 집합)를 반환해야 합니다.

덜 야심 찬 프레임워크는 자체 이벤트 루프를 구현하지 않고 loop.set_task_factory()를 호출하여 Task 클래스를 대체할 수 있습니다.

이벤트 루프 API는 스레드와의 제한적인 상호 운용성을 제공합니다. 실행기에 함수를 제출하고 이벤트 루프와 호환되는 Future를 반환하는 API(PEP 3148 참조)가 있으며, 다른 스레드에서 스레드 안전한 방식으로 이벤트 루프에 콜백을 예약하는 메서드도 있습니다.

트랜스포트와 프로토콜

Twisted에 익숙하지 않은 분들을 위해 트랜스포트와 프로토콜의 관계를 간단히 설명할 필요가 있습니다. 가장 높은 수준에서 트랜스포트는 바이트가 어떻게 전송되는지에 관여하는 반면, 프로토콜은 전송할 바이트가 무엇인지(그리고 어느 정도는 언제인지)를 결정합니다.

같은 내용을 다르게 표현하면, 트랜스포트는 소켓(또는 이와 유사한 I/O 엔드포인트)을 추상화한 것이고, 프로토콜은 트랜스포트의 관점에서 애플리케이션을 추상화한 것입니다.

또 다른 관점에서 보면 트랜스포트와 프로토콜 인터페이스는 함께 네트워크 I/O와 프로세스 간 I/O를 사용하기 위한 추상 인터페이스를 정의합니다.

트랜스포트 객체와 프로토콜 객체 사이에는 거의 항상 1:1 관계가 있습니다. 프로토콜은 데이터를 보내기 위해 트랜스포트 메서드를 호출하고, 트랜스포트는 수신한 데이터를 전달하기 위해 프로토콜 메서드를 호출합니다. 트랜스포트 메서드와 프로토콜 메서드는 어느 쪽도 “block”하지 않습니다. 이벤트를 시작한 다음 반환합니다.

가장 일반적인 전송 유형은 양방향 스트림 전송입니다. 이는 버퍼링된 스트림 한 쌍(각 방향에 하나씩)을 나타내며, 각 스트림은 바이트 시퀀스를 전송합니다. 양방향 스트림 전송의 가장 일반적인 예는 아마도 TCP 연결일 것입니다. 또 다른 일반적인 예는 SSL/TLS 연결입니다. 그러나 이런 방식으로 볼 수 있는 다른 것들도 있으며, 예를 들어 SSH 세션이나 UNIX 파이프 한 쌍이 있습니다. 일반적으로 서로 다른 전송 구현은 많지 않으며, 대부분은 이벤트 루프 구현과 함께 제공됩니다. 그러나 모든 전송을 이벤트 루프 메서드를 호출하여 생성해야 한다는 요구 사항은 없습니다. 서드 파티 모듈은 새로운 전송을 구현하고, 이벤트 루프를 인자로 단순히 받거나 get_event_loop()를 호출하는 생성자 또는 팩토리 함수를 제공할 수 있습니다.

전송은 소켓을 사용할 필요가 없으며, TCP를 사용하는 경우에도 마찬가지입니다. 소켓은 플랫폼별 구현 세부 사항입니다.

양방향 스트림 전송에는 두 개의 “끝”이 있습니다. 한쪽 끝은 네트워크(또는 다른 프로세스나 자신이 감싸는 어떤 저수준 인터페이스)와 통신하고, 다른 쪽 끝은 프로토콜과 통신합니다. 전자는 전송을 구현하는 데 필요한 모든 API를 사용하지만, 전송과 프로토콜 사이의 인터페이스는 이 PEP에서 표준화합니다.

프로토콜은 HTTP나 SMTP와 같은 일종의 “애플리케이션 수준” 프로토콜을 나타낼 수 있으며, 여러 프로토콜이 공유하는 추상화나 전체 애플리케이션을 구현할 수도 있습니다. 프로토콜의 기본 인터페이스는 전송과 연결됩니다. 일부 널리 사용되는 프로토콜(및 기타 추상화)에는 표준 구현이 있을 수 있지만, 애플리케이션은 흔히 사용자 지정 프로토콜을 구현합니다. PyPI에서 다운로드하여 설치할 수 있는 유용한 서드 파티 프로토콜 구현 라이브러리를 갖추는 것도 타당합니다.

전송과 프로토콜의 일반적인 개념에는 다른 통신 추상화를 전송이 감싸는 기타 인터페이스도 포함됩니다. 예로는 데이터그램(예: UDP)을 송수신하기 위한 인터페이스나 서브프로세스 관리자가 있습니다. 관심사 분리는 양방향 스트림 전송 및 프로토콜의 경우와 동일하지만, 전송과 프로토콜 사이의 구체적인 인터페이스는 각 경우에 다릅니다.

다양한 표준 전송 및 프로토콜 유형에 의해 정의되는 인터페이스의 세부 사항은 뒤에서 설명합니다.

이벤트 루프 인터페이스 사양

이벤트 루프 정책: 현재 이벤트 루프 가져오기 및 설정하기

이벤트 루프 관리는 이벤트 루프 정책에 의해 제어되며, 이 정책은 전역(프로세스별) 객체입니다. 기본 정책과 정책을 변경하기 위한 API가 있습니다. 정책은 컨텍스트의 개념을 정의하며, 컨텍스트별로 별도의 이벤트 루프를 관리합니다. 기본 정책에서 컨텍스트의 개념은 현재 스레드로 정의됩니다.

특정 플랫폼이나 프로그래밍 프레임워크는 해당 플랫폼 또는 프레임워크 사용자의 기대에 더 적합한 방식으로 기본 정책을 변경할 수 있습니다. 여러 활성 프레임워크가 기본 정책을 재정의하려 할 때 정의되지 않은 동작을 피하려면, 이러한 플랫폼이나 프레임워크는 해당 정책과 초기화 순서 중 정책이 설정되는 시점을 문서화해야 합니다. (아래의 “임베디드 이벤트 루프”도 참조하십시오.)

현재 컨텍스트의 이벤트 루프를 가져오려면 get_event_loop()를 사용하십시오. 이는 아래에 지정된 인터페이스를 구현하는 이벤트 루프 객체를 반환하거나, 현재 컨텍스트에 이벤트 루프가 설정되지 않았고 현재 정책도 이벤트 루프를 생성하도록 지정하지 않은 경우 예외를 발생시킵니다. 결코 None을 반환해서는 안 됩니다.

현재 컨텍스트의 이벤트 루프를 설정하려면 set_event_loop(event_loop)를 사용하십시오. 여기서 event_loop는 이벤트 루프 객체, 즉 AbstractEventLoop의 인스턴스이거나 None입니다. 현재 이벤트 루프를 None으로 설정해도 되며, 이 경우 이후 get_event_loop()를 호출하면 예외가 발생합니다. 이는 기본 이벤트 루프의 존재에 의존해서는 안 되는 코드를 테스트할 때 유용합니다.

컨텍스트에 따라 get_event_loop()이 서로 다른 이벤트 루프 객체를 반환할 것으로 예상합니다(실제로 이것이 컨텍스트의 정의입니다). 아무것도 설정되어 있지 않고 정책에서 생성을 허용하는 경우 새 이벤트 루프 객체를 생성할 수 있습니다. 기본 정책은 주 스레드에서만(threading.py에서 정의하는 주 스레드는 특수 서브클래스를 사용합니다), 그리고 set_event_loop()가 호출된 적이 없는 상태에서 get_event_loop()가 호출된 경우에만 새 이벤트 루프를 생성합니다. (이 상태를 재설정하려면 정책을 재설정하십시오.) 다른 스레드에서는 이벤트 루프를 명시적으로 설정해야 합니다. 다른 정책은 다르게 동작할 수 있습니다. 기본 정책에 따른 이벤트 루프 생성은 지연 방식입니다. 즉, 필요한 경우 현재 정책에서 지정하는 대로 get_event_loop()를 처음 호출할 때 이벤트 루프 인스턴스를 생성합니다.

단위 테스트와 기타 특수한 경우를 위해 세 번째 정책 함수인 new_event_loop()가 있으며, 이 함수는 정책의 기본 규칙에 따라 새 이벤트 루프 객체를 생성하여 반환합니다. 이를 현재 이벤트 루프로 만들려면 해당 객체를 사용하여 set_event_loop()를 호출해야 합니다.

이벤트 루프 정책을 변경하려면 set_event_loop_policy(policy)를 호출하십시오. 여기서 policy는 이벤트 루프 정책 객체이거나 None입니다. None이 아닌 경우 정책 객체는 AbstractEventLoopPolicy의 인스턴스여야 하며, 위에서 설명한 함수처럼 동작하는 get_event_loop(), set_event_loop(loop)new_event_loop()메서드를 정의해야 합니다.

정책 값으로 None을 전달하면 기본 이벤트 루프 정책이 복원됩니다(플랫폼이나 프레임워크에서 설정한 대체 기본값을 재정의합니다). 기본 이벤트 루프 정책은 DefaultEventLoopPolicy클래스의 인스턴스입니다. 현재 이벤트 루프 정책 객체는 get_event_loop_policy()를 호출하여 가져올 수 있습니다.

TBD: 서브프로세스 처리에서 자식 감시자와 UNIX 특이 사항을 설명할 예정입니다.

이벤트 루프 명시적으로 전달하기

전역 또는 스레드별 기본 이벤트 루프에 의존하지 않고 이벤트 루프를 사용하는 코드를 작성할 수 있습니다. 이를 위해 현재 이벤트 루프에 액세스해야 하는 모든 API는(이벤트 클래스의 메서드가 아닌 경우) loop이라는 선택적 키워드 인자를 받습니다. 이 인자가 None이거나 지정되지 않으면 이러한 API는 기본 이벤트 루프를 가져오기 위해 get_event_loop()를 호출하지만, loop 키워드 인자가 이벤트 루프 객체로 설정되어 있으면 해당 이벤트 루프를 사용하고 호출하는 다른 이러한 API에 이를 전달합니다. 예를 들어 Future(loop=my_loop)는 이벤트 루프 my_loop에 연결된 Future를 생성합니다. 기본 현재 이벤트가 None이면 loop 키워드 인자는 사실상 필수입니다.

명시적으로 전달된 이벤트 루프도 현재 스레드에 속해야 한다는 점에 유의하십시오. loop 키워드 인자가 이벤트 루프를 사용할 수 있는 방법의 제약을 마법처럼 변경하지는 않습니다.

시간 지정

Python에서 일반적으로 그렇듯이 모든 타임아웃, 간격 및 지연 시간은 초 단위로 측정하며, 정수 또는 부동 소수점 수일 수 있습니다. 그러나 절대 시간은 POSIX 타임스탬프로 지정되지 않습니다. 시계의 정확도, 정밀도 및 에포크는 구현에 따라 결정됩니다.

기본 구현은 time.monotonic()을 사용합니다. 이 선택의 영향에 대해서는 책을 쓸 수도 있습니다. 표준 라이브러리 time모듈의 문서를 읽는 것이 좋습니다.

임베디드 이벤트 루프

일부 플랫폼에서는 시스템이 이벤트 루프를 제공합니다. 이러한 루프는 사용자 코드가 시작될 때 이미 실행 중일 수 있으며, 프로그램을 종료하지 않고는 이를 중지하거나 닫을 방법이 없을 수 있습니다. 이 경우 이벤트 루프를 시작하고 중지하고 닫는 메서드를 구현하지 못할 수 있으며, is_running()은 항상 True를 반환할 수 있습니다.

이벤트 루프 클래스

EventLoop이라는 실제 클래스는 없습니다. 모든 메서드를 구현 없이 정의하며 주로 문서 역할을 하는 AbstractEventLoop 클래스가 있습니다. 다음 구체 클래스가 정의되어 있습니다:

  • SelectorEventLoopselectors 모듈을 기반으로 전체 API를 구현한 구체 구현입니다(Python 3.4에서 새로 추가되었습니다). 생성자는 하나의 선택적 인자인 selectors.Selector 객체를 받습니다. 기본적으로 selectors.DefaultSelector의 인스턴스를 생성하여 사용합니다.
  • ProactorEventLoop는 I/O 이벤트 처리 및 신호 처리 메서드를 제외한 API를 구현한 구체 구현입니다. Windows에서만(또는 “overlapped I/O”에 대해 유사한 API를 지원하는 다른 플랫폼에서만) 정의됩니다. 생성자는 하나의 선택적 인자인 Proactor 객체를 받습니다. 기본적으로 IocpProactor 클래스의 인스턴스를 생성하여 사용합니다. (IocpProactor 클래스는 이 PEP에서 규정하지 않으며, ProactorEventLoop 클래스의 구현 세부 사항일 뿐입니다.)

이벤트 루프 메서드 개요

규격을 준수하는 이벤트 루프의 메서드는 여러 범주로 나뉩니다. 첫 번째 범주 집합은 모든 규격 준수 이벤트 루프 구현에서 지원되어야 합니다. 단, 임베디드 이벤트 루프는 시작, 중지 및 종료를 위한 메서드를 구현하지 않을 수 있습니다. (그러나 부분적으로 규격을 준수하는 이벤트 루프라도 없는 것보다는 낫습니다. :-)

  • 시작, 중지 및 종료: run_forever(), run_until_complete(), stop(), is_running(), close(), is_closed().
  • 기본 및 시간 지정 콜백: call_soon(), call_later(), call_at(), time().
  • 스레드 상호 작용: call_soon_threadsafe(), run_in_executor(), set_default_executor().
  • 인터넷 이름 조회: getaddrinfo(), getnameinfo().
  • 인터넷 연결: create_connection(), create_server(), create_datagram_endpoint().
  • 래핑된 소켓 메서드: sock_recv(), sock_sendall(), sock_connect(), sock_accept().
  • 태스크 및 퓨처 지원: create_future(), create_task(), set_task_factory(), get_task_factory().
  • 오류 처리: get_exception_handler(), set_exception_handler(), default_exception_handler(), call_exception_handler().
  • 디버그 모드: get_debug(), set_debug().

두 번째 범주 집합은 규격을 준수하는 이벤트 루프 구현에서 지원될 수도 있습니다. 지원되지 않으면 NotImplementedError를 발생시킵니다. (기본 구현에서 UNIX 시스템의 SelectorEventLoop는 이러한 모든 항목을 지원하고, Windows의 SelectorEventLoop는 I/O 이벤트 처리 범주를 지원하며, Windows의 ProactorEventLoop는 파이프 및 서브프로세스 범주를 지원합니다.)

  • I/O 콜백: add_reader(), remove_reader(), add_writer(), remove_writer().
  • 파이프 및 서브프로세스: connect_read_pipe(), connect_write_pipe(), subprocess_shell(), subprocess_exec().
  • 신호 콜백: add_signal_handler(), remove_signal_handler().

이벤트 루프 메서드

시작, 중지 및 종료

(종료되지 않은) 이벤트 루프는 실행 중 또는 중지됨이라는 두 가지 상태 중 하나일 수 있습니다. 다음 메서드는 이벤트 루프의 시작 및 중지를 처리합니다:

  • run_forever(). stop()이 호출될 때까지 이벤트 루프를 실행합니다. 이벤트 루프가 이미 실행 중일 때는 호출할 수 없습니다. (이 이름이 긴 이유는 부분적으로 이 PEP의 이전 버전에서 run()의 동작이 달랐던 점과 혼동하지 않도록 하기 위해서이고, 부분적으로 이미 run()이라는 메서드를 가진 API가 너무 많기 때문이며, 또 부분적으로는 어차피 이것이 호출될 장소가 많지 않아야 하기 때문입니다.)
  • run_until_complete(future). Future가 완료될 때까지 이벤트 루프를 실행합니다. Future가 완료되면 그 결과를 반환하거나 예외를 발생시킵니다. 이벤트 루프가 이미 실행 중일 때는 호출할 수 없습니다. 매개변수가 코루틴이면 이 메서드는 새 Task 객체를 생성합니다.
  • stop(). 가능한 한 편리한 시점에 이벤트 루프를 중지합니다. 이후 run_forever()또는 run_until_complete()로 루프를 다시 시작해도 괜찮습니다. 이렇게 해도 예약된 콜백이 손실되지 않습니다. 참고: stop()은 정상적으로 반환되며 현재 콜백은 계속 실행될 수 있습니다. 이 시점 이후 이벤트 루프가 얼마나 빨리 중지되는지는 구현에 달려 있지만, 의도는 I/O 폴링 직전에 중지하고 향후에 예약된 콜백을 실행하지 않는 것입니다. 구현이 지닌 주요 재량은 중지하기 전에 “준비 큐”(이미 call_soon()으로 예약된 콜백)를 얼마나 처리할지입니다.
  • is_running(). 이벤트 루프가 현재 실행 중이면 True를, 중지되어 있으면 False를 반환합니다.
  • close(). 이벤트 루프를 닫고, epoll()이나 kqueue()에서 사용하는 파일 디스크립터 및 기본 실행기와 같은 보유 중인 리소스를 해제합니다. 이벤트 루프가 실행 중일 때는 호출해서는 안 됩니다. 호출한 후에는 이벤트 루프를 다시 사용해서는 안 됩니다. 여러 번 호출할 수 있으며, 이후 호출은 아무 작업도 수행하지 않습니다.
  • is_closed(). 이벤트 루프가 닫혀 있으면 True를, 그렇지 않으면 False를 반환합니다. (주로 오류 보고를 위한 메서드이므로 이 메서드를 기반으로 기능을 구현하지 마십시오.)

기본 콜백

동일한 이벤트 루프에 연결된 콜백은 엄격하게 직렬화됩니다. 하나의 콜백이 완료된 후에야 다음 콜백이 호출됩니다. 이는 중요한 보장입니다. 두 개 이상의 콜백이 공유 상태를 사용하거나 수정할 때 각 콜백이 실행되는 동안에는 다른 콜백이 공유 상태를 변경하지 않는다는 것이 보장됩니다.

  • call_soon(callback, *args). 가능한 한 빨리 호출되도록 콜백을 예약합니다. 콜백을 나타내는 Handle을 반환하며, 이 핸들의 cancel()메서드를 사용하여 콜백을 취소할 수 있습니다. 콜백이 예약된 순서대로 호출되도록 보장합니다.
  • call_later(delay, callback, *args). 취소되지 않는 한, 약 delay초 후에 callback(*args)가 한 번 호출되도록 예약합니다. 콜백을 나타내는 Handle을 반환하며, 해당 cancel()메서드를 사용하여 콜백을 취소할 수 있습니다. 과거 시점 또는 정확히 같은 시점에 예약된 콜백은 정의되지 않은 순서로 호출됩니다.
  • call_at(when, callback, *args). 이는 call_later()와 비슷하지만, 시간은 절대 시간으로 표현합니다. 이와 유사한 Handle을 반환합니다. 간단한 동등 관계가 있습니다. loop.call_later(delay, callback, *args)loop.call_at(loop.time() + delay, callback, *args)와 같습니다.
  • time(). 이벤트 루프의 시계에 따른 현재 시간을 반환합니다. 이는 time.time()이거나 time.monotonic()이거나 다른 시스템별 시계일 수 있지만, 어떤 기준 시점부터 약 1초 단위로 시간을 나타내는 부동 소수점 수를 반환해야 합니다. (완벽한 시계는 없습니다. PEP 418을 참조하십시오.)

참고: 이 PEP의 이전 버전에서는 일정한 간격으로 콜백을 호출하겠다고 약속한 call_repeatedly()라는 메서드를 정의했습니다. 이러한 함수의 설계가 지나치게 구체화되어 있기 때문에 이를 철회했습니다. 한편으로는 call_later()를 사용하여 스스로를 다시 예약하는 콜백으로 간단한 타이머 루프를 쉽게 모방할 수 있으며, 루프와 sleep()호출을 포함하는 코루틴(아래에 설명하는 모듈의 최상위 함수)을 작성하는 것도 쉽습니다. 다른 한편으로는 정확한 시간 측정이 복잡하기 때문에 이를 잘 모르는 사용자에게는 많은 함정과 문제가 있으며(PEP 418을 참조하십시오), 사용 사례에 따라 가장자리 사례에서 요구되는 동작도 다릅니다. 모든 경우에 완벽하게 안전한 API를 이러한 목적으로 제공하는 것은 불가능하므로, 애플리케이션 설계자가 어떤 종류의 타이머 루프를 구현할지 직접 결정하도록 하는 것이 더 낫다고 간주합니다.

스레드 상호 작용

  • call_soon_threadsafe(callback, *args). call_soon(callback, *args)와 같지만, 이벤트 루프가 I/O를 기다리며 차단된 상태에서 다른 스레드가 호출하면 이벤트 루프의 차단을 해제합니다. Handle을 반환합니다. 이는 다른 스레드에서 호출해도 안전한 유일한 메서드입니다. (스레드 안전한 방식으로 나중에 콜백을 예약하려면 loop.call_soon_threadsafe(loop.call_later, when, callback, *args)를 사용할 수 있습니다.) 참고: 신호 처리기에서 호출하는 것은 안전하지 않습니다(잠금을 사용할 수 있기 때문입니다). 실제로 어떤 API도 신호에 안전하지 않습니다. 신호를 처리하려면 아래에 설명된 add_signal_handler()를 사용하십시오.
  • run_in_executor(executor, callback, *args). 실행기에서 callback(*args)를 호출하도록 예약합니다(PEP 3148을 참조하십시오). 성공 시 해당 호출의 반환 값이 결과가 되는 asyncio.Future 인스턴스를 반환합니다. 이는 wrap_future(executor.submit(callback, *args))와 동등합니다. executorNone이면 set_default_executor()로 설정한 기본 실행기를 사용합니다. 아직 기본 실행기가 설정되지 않았다면, 기본 스레드 수를 사용하는 ThreadPoolExecutor를 생성하여 기본 실행기로 설정합니다. (이 경우 기본 구현은 5개의 스레드를 사용합니다.)
  • set_default_executor(executor). run_in_executor()에서 사용할 기본 실행기를 설정합니다. 기본 실행기를 재설정하려면 인자는 PEP 3148 Executor 인스턴스이거나 None이어야 합니다.

Futures 관련 섹션에 설명된 wrap_future()함수도 참조하십시오.

인터넷 이름 조회

이름 조회로 인해 차단될 위험 없이 소켓을 주소에 연결하거나 바인딩하려는 경우 이러한 메서드가 유용합니다. 일반적으로 create_connection(), create_server() 또는 create_datagram_endpoint()에 의해 암묵적으로 호출됩니다.

  • getaddrinfo(host, port, family=0, type=0, proto=0, flags=0). socket.getaddrinfo() 함수와 유사하지만 Future를 반환합니다. 성공 시 Future의 결과는 socket.getaddrinfo()가 반환하는 것과 동일한 형식의 목록, 즉 (address_family, socket_type, socket_protocol, canonical_name, address) 목록입니다. 여기서 IPv4 주소의 경우 address는 2-튜플 (ipv4_address, port)이고, IPv6 주소의 경우 4-튜플 (ipv6_address, port, flow_info, scope_id)입니다. family 인자가 0이거나 지정되지 않은 경우 반환되는 목록에는 IPv4 주소와 IPv6 주소가 섞여 있을 수 있으며, 그렇지 않으면 반환되는 주소는 family 값에 의해 제한됩니다(protoflags의 경우에도 동일합니다). 기본 구현은 run_in_executor()를 사용하여 socket.getaddrinfo()를 호출하지만, 다른 구현에서는 자체 DNS 조회를 구현할 수 있습니다. 선택적 인자는 반드시 키워드 인자로 지정해야 합니다.

    참고: 구현은 전체 socket.getaddrinfo() 인터페이스의 일부만 구현할 수 있습니다. 예를 들어 기호 포트 이름을 지원하지 않거나 type, protoflags 인자를 무시하거나 불완전하게 구현할 수 있습니다. 그러나 typeproto를 무시하는 경우 전달된 인자 값은 반환 튜플의 socket_typesocket_protocol 요소에 변경 없이 복사되어야 합니다. (IPv4 주소와 IPv6 주소는 서로 다르게 조회해야 하므로 family는 무시할 수 없습니다. family에 허용되는 값은 socket.AF_UNSPEC (0), socket.AF_INETsocket.AF_INET6뿐이며, 후자는 플랫폼에서 정의된 경우에만 허용됩니다.)

  • getnameinfo(sockaddr, flags=0). socket.getnameinfo()와 유사하지만 Future를 반환합니다. 성공 시 Future의 결과는 (host, port) 튜플입니다. getaddrinfo()와 동일한 구현 관련 참고 사항이 적용됩니다.

인터넷 연결

이는 인터넷 연결을 관리하기 위한 고수준 인터페이스입니다. 셀렉터 기반 이벤트 루프와 프로액터 기반 이벤트 루프의 차이를 추상화하므로, 이에 대응하는 저수준 인터페이스보다 이러한 인터페이스를 사용하는 것이 권장됩니다.

스트림 연결의 클라이언트 측과 서버 측은 동일한 트랜스포트 및 프로토콜 인터페이스를 사용한다는 점에 유의하십시오. 그러나 데이터그램 엔드포인트는 서로 다른 트랜스포트 및 프로토콜 인터페이스를 사용합니다.

  • create_connection(protocol_factory, host, port, <options>). 지정된 인터넷 호스트 및 포트에 스트림 연결을 생성합니다. 이는 일반적으로 연결의 클라이언트 측에서 호출되는 태스크입니다. 연결을 나타내기 위해 구현에 따라 달라지는 양방향 스트림 Transport를 생성한 다음, protocol_factory()를 호출하여 사용자의 프로토콜 구현을 인스턴스화하거나 가져오고, 마지막으로 둘을 연결합니다. (Transport와 프로토콜의 정의는 아래를 참조하십시오.) 사용자의 프로토콜 구현은 인자 없이 protocol_factory()를 호출하여 생성하거나 가져옵니다(*). 성공 시 코루틴의 결과는 (transport, protocol) 쌍입니다. 실패로 인해 성공적인 연결을 생성할 수 없는 경우 적절한 예외가 발생합니다. 코루틴이 완료될 때 프로토콜의 connection_made() 메서드는 아직 호출되지 않았으며, 연결 핸드셰이크가 완료되면 호출된다는 점에 유의하십시오.

    (*) protocol_factory가 클래스여야 한다는 요구 사항은 없습니다. 프로토콜 클래스의 생성자에 특정 인자를 전달해야 하는 경우 lambda를 사용할 수 있습니다. 이전에 생성된 프로토콜 인스턴스를 반환하는 간단한 lambda를 전달할 수도 있습니다.

    <options>는 모두 선택적 키워드 인자를 사용하여 지정합니다.

    • ssl: True를 전달하여 SSL/TLS 전송을 생성합니다(기본적으로 일반 TCP 전송이 생성됩니다). 또는 사용할 기본 SSL 컨텍스트 객체를 재정의하려면 ssl.SSLContext 객체를 전달합니다. 기본 컨텍스트가 생성되는 경우 합리적인 기본값을 구성하는 것은 구현에 달려 있습니다. 참조 구현은 현재 PROTOCOL_SSLv23을 사용하고 OP_NO_SSLv2 옵션을 설정하며, set_default_verify_paths()를 호출하고 verify_modeCERT_REQUIRED로 설정합니다. 또한 컨텍스트(기본 컨텍스트이든 아니든)가 verify_modeCERT_REQUIRED또는 CERT_OPTIONAL로 지정하고 호스트 이름이 주어진 경우, 성공적인 핸드셰이크 직후 ssl.match_hostname(peercert, hostname)을 호출하며, 이 호출에서 예외가 발생하면 연결이 닫힙니다. (이 동작을 피하려면 verify_modeCERT_NONE으로 설정된 SSL 컨텍스트를 전달하십시오. 그러나 이는 보안이 확보되지 않으며, 예를 들어 중간자 공격에 취약하다는 의미입니다.)
    • family, proto, flags: getaddrinfo()에 전달할 주소 패밀리, 프로토콜 및 플래그입니다. 이들은 모두 0으로 기본 설정되며, 이는 “지정되지 않음”을 의미합니다. (소켓 유형은 항상 SOCK_STREAM입니다.) 이러한 값 중 지정되지 않은 값이 있으면 getaddrinfo() 메서드가 적절한 값을 선택합니다. 참고: proto는 고수준 프로토콜 개념이나 protocol_factory 인자와는 아무 관련이 없습니다.
    • sock: host, port, family, protoflags 인자를 사용하는 대신 사용할 선택적 소켓입니다. 이 값이 주어지면 hostport를 명시적으로 None으로 설정해야 합니다.
    • local_addr: 주어진 경우 소켓을 로컬에 바인딩하는 데 사용되는 (host, port) 튜플입니다. 이는 거의 필요하지 않지만, 멀티홈 서버에서는 특정 주소에서 연결이 시작되도록 강제해야 하는 경우가 가끔 있습니다. 이렇게 하면 됩니다. 호스트와 포트는 getaddrinfo()를 사용하여 조회합니다.
    • server_hostname: 이는 SSL/TLS를 사용할 때만 관련이 있으며, ssl이 설정되지 않은 경우 사용해서는 안 됩니다. ssl이 설정되면 검증할 호스트 이름을 설정하거나 재정의합니다. 기본적으로 host 인자의 값이 사용됩니다. host가 비어 있으면 기본값이 없으므로 server_hostname에 값을 전달해야 합니다. 호스트 이름 검증을 비활성화하려면(이는 심각한 보안 위험입니다) 여기에는 빈 문자열을 전달하고, ssl 인자로 verify_modessl.CERT_NONE으로 설정된 ssl.SSLContext 객체를 전달해야 합니다.
  • create_server(protocol_factory, host, port, <options>). 연결을 수락하는 서버 실행 루프에 들어갑니다. 이는 서버 실행 루프가 서비스를 제공하도록 설정되면 완료되는 코루틴입니다. 반환값은 서버 실행 루프를 제어된 방식으로 중지하는 데 사용할 수 있는 Server 객체입니다(아래 참조). 지정된 주소가 IPv4와 IPv6 연결을 모두 허용하는 경우 여러 소켓을 바인딩할 수 있습니다.

    연결이 수락될 때마다 protocol_factory를 인자 없이(**) 호출하여 프로토콜을 생성하고, 연결의 네트워크 측을 나타내는 양방향 스트림 전송을 생성한 다음 protocol.connection_made(transport)를 호출하여 둘을 연결합니다.

    (**) create_connection()에 관한 이전 각주를 참조하십시오. 그러나 protocol_factory()는 새로 들어오는 각 연결마다 한 번씩 호출되므로 호출될 때마다 새 프로토콜 객체를 반환해야 합니다.

    <options>는 모두 선택적 키워드 인자를 사용하여 지정합니다.

    • ssl: 사용할 기본 SSL 컨텍스트 객체를 재정의하려면 ssl.SSLContext 객체(또는 동일한 인터페이스를 가진 객체)를 전달합니다. (create_connection()의 경우와 달리 여기서 True를 전달하는 것은 의미가 없습니다. 인증서와 키를 지정하려면 SSLContext 객체가 필요합니다.)
    • backlog: listen() 호출에 전달할 백로그 값입니다. 기본값은 구현에 따라 다르며, 기본 구현에서 기본값은 100입니다.
    • reuse_address: 소켓에서 SO_REUSEADDR 옵션을 설정할지 여부입니다. UNIX에서는 기본값이 True이고, Windows에서는 False입니다.
    • family, flags: getaddrinfo()에 전달할 주소 패밀리와 플래그입니다. 패밀리의 기본값은 AF_UNSPEC이며, 플래그의 기본값은 AI_PASSIVE입니다. (소켓 유형은 항상 SOCK_STREAM이며, 소켓 프로토콜은 항상 0으로 설정하여 getaddrinfo()가 선택하도록 합니다.)
    • sock: host, port, familyflags 인자를 사용하는 대신 사용할 선택적 소켓입니다. 이를 지정하는 경우 hostport를 명시적으로 None으로 설정해야 합니다.
  • create_datagram_endpoint(protocol_factory, local_addr=None, remote_addr=None, <options>)입니다. 데이터그램(일반적으로 UDP 패킷)을 송수신하기 위한 엔드포인트를 생성합니다. 데이터그램 트래픽의 특성상 일반적으로 하나의 엔드포인트가 클라이언트와 서버 역할을 모두 수행하므로, 클라이언트 측과 서버 측을 설정하기 위한 별도의 호출은 없습니다. 성공하면 (transport, protocol) 쌍을 반환하고, 실패하면 예외를 발생시키는 코루틴입니다. 코루틴이 성공적으로 반환되면, 데이터그램을 수신하거나 소켓이 닫힐 때마다 트랜스포트가 프로토콜에서 콜백을 호출합니다. 데이터그램을 전송하기 위해 프로토콜의 메서드를 호출하는 것은 프로토콜의 책임입니다. 반환되는 트랜스포트는 DatagramTransport입니다. 반환되는 프로토콜은 DatagramProtocol입니다. 이에 대해서는 뒤에서 설명합니다.

    필수 위치 인자:

    • protocol_factory: 인자 없이 정확히 한 번 호출되어 반환할 프로토콜 객체를 생성하는 클래스 또는 팩토리 함수입니다. 데이터그램 트랜스포트와 프로토콜 간의 인터페이스는 아래에 설명되어 있습니다.

    위치 인자 또는 키워드 인자로 지정할 수 있는 선택적 인자:

    • local_addr: 소켓이 바인딩될 주소를 나타내는 선택적 튜플입니다. 지정하는 경우 (host, port) 쌍이어야 합니다. 이 값은 확인을 위해 getaddrinfo()에 전달되고, 그 결과는 생성된 소켓의 bind() 메서드에 전달됩니다. getaddrinfo()가 둘 이상의 주소를 반환하면 차례로 시도합니다. 생략하면 bind() 호출은 수행되지 않습니다.
    • remote_addr: 소켓이 “연결될” 주소를 나타내는 선택적 튜플입니다. (데이터그램 연결이라는 것은 존재하지 않으므로, 이는 발신 데이터그램의 대상 주소에 대한 기본값만 지정합니다.) 지정하는 경우 (host, port) 쌍이어야 합니다. 이 값은 확인을 위해 getaddrinfo()에 전달되고, 그 결과는 생성된 소켓과 함께 sock_connect()에 전달됩니다. getaddrinfo()가 둘 이상의 주소를 반환하면 차례로 시도합니다. 생략하면 sock_connect() 호출은 수행되지 않습니다.

    <options>는 모두 선택적 키워드 인자를 사용하여 지정합니다.

    • family, proto, flags: getaddrinfo()에 전달할 주소 패밀리, 프로토콜 및 플래그입니다. 이들의 기본값은 모두 0이며, 이는 “지정되지 않음”을 의미합니다. (소켓 유형은 항상 SOCK_DGRAM입니다.) 이러한 값 중 지정되지 않은 것이 있으면 getaddrinfo() 메서드가 적절한 값을 선택합니다.

    local_addrremote_addr가 모두 있으면, 주소 패밀리가 일치하는 로컬 주소와 원격 주소의 모든 조합을 시도한다는 점에 유의하십시오.

래핑된 소켓 메서드

소켓에서 비동기 I/O를 수행하기 위한 다음 메서드는 일반적인 용도가 아닙니다. 주로 ProactorEventLoop 클래스를 통해 IOCP와 함께 작동하는 전송 구현을 위한 것입니다. 그러나 다른 이벤트 루프 유형에도 쉽게 구현할 수 있으므로, 이를 요구하지 않을 이유가 없습니다. 소켓 인자는 비블로킹 소켓이어야 합니다.

  • sock_recv(sock, n). 소켓 sock에서 최대 n바이트를 수신합니다. 성공 시 결과가 바이트 객체가 되는 Future를 반환합니다.
  • sock_sendall(sock, data). sock 소켓으로 data 바이트를 전송합니다. 성공 시 결과가 None이 되는 Future를 반환합니다. 참고: 이 메서드의 의미 체계와 시그니처가 표준 라이브러리 소켓 메서드 send()가 아니라 sendall()의 것과 일치함을 나타내기 위해 이름에 send 대신 sendall을 사용합니다.
  • sock_connect(sock, address). 지정된 주소에 연결합니다. 성공 시 결과가 None이 되는 Future를 반환합니다.
  • sock_accept(sock). 소켓에서 연결을 수락합니다. 소켓은 수신 대기 모드이고 주소에 바인딩되어 있어야 합니다. 성공 시 결과가 튜플 (conn, peer)이며, conn은 연결된 비블로킹 소켓이고 peer는 피어 주소인 Future를 반환합니다.

I/O 콜백

이러한 메서드는 주로 셀렉터와 함께 작동하는 전송 구현을 위한 것입니다. 이러한 메서드는 SelectorEventLoop에서는 구현되지만 ProactorEventLoop에서는 구현되지 않습니다. 사용자 지정 이벤트 루프 구현은 이러한 메서드를 구현할 수도 있고 구현하지 않을 수도 있습니다.

아래의 fd 인자는 정수 파일 디스크립터이거나, 정수 파일 디스크립터를 래핑하는 fileno() 메서드가 있는 “파일과 유사한” 객체일 수 있습니다. 모든 파일과 유사한 객체 또는 파일 디스크립터가 허용되는 것은 아닙니다. 소켓(및 소켓 파일 디스크립터)은 항상 허용됩니다. Windows에서는 다른 유형이 지원되지 않습니다. UNIX에서는 파이프와 일부 TTY 장치도 지원될 수 있지만, 디스크 파일은 지원되지 않습니다. 지원되는 특수 파일 유형은 플랫폼과 셀렉터 구현에 따라 다를 수 있습니다. (실험적으로 selectpoll에서는 지원되지만 kqueue에서는 지원되지 않는 OS X의 의사 TTY 유형이 하나 이상 있으며, 이는 Emacs 셸 창에서 사용됩니다.)

  • add_reader(fd, callback, *args). 파일 디스크립터 fd가 읽기에 준비된 것으로 판단될 때마다 callback(*args)가 호출되도록 설정합니다. 동일한 파일 디스크립터에 대해 add_reader()를 다시 호출하면 동일한 파일 디스크립터에 대해 remove_reader()를 호출하는 것과 같습니다.
  • add_writer(fd, callback, *args). add_reader()와 마찬가지이지만, 읽기 대신 쓰기를 위해 콜백을 등록합니다.
  • remove_reader(fd). 설정되어 있는 경우 파일 디스크립터 fd의 현재 읽기 콜백을 취소합니다. 파일 디스크립터에 현재 설정된 콜백이 없으면 아무 작업도 수행하지 않고 False를 반환합니다. 그렇지 않으면 콜백 설정을 제거하고 True를 반환합니다.
  • remove_writer(fd). 이는 add_writer()에 대한 관계가 remove_reader()add_reader()의 관계와 같습니다.

파이프와 서브프로세스

이러한 메서드는 UNIX에서는 SelectorEventLoop에서, Windows에서는 ProactorEventLoop에서 지원됩니다.

파이프와 서브프로세스에 사용되는 트랜스포트와 프로토콜은 일반 스트림 연결에 사용되는 것과 다릅니다. 이에 대해서는 뒤에서 설명합니다.

아래의 각 메서드에는 create_connection()과 유사한 protocol_factory인자가 있으며, 반환할 프로토콜 객체를 생성하기 위해 인자 없이 정확히 한 번 호출됩니다.

각 메서드는 성공하면 (transport, protocol)쌍을 반환하는 코루틴이며, 실패하면 예외를 발생시킵니다.

  • connect_read_pipe(protocol_factory, pipe): 비블로킹 모드여야 하는 UNIX 파이프의 읽기 끝을 감싸는 파일 유사 객체로부터 단방향 스트림 연결을 생성합니다. 반환되는 트랜스포트는 ReadTransport입니다.
  • connect_write_pipe(protocol_factory, pipe): 비블로킹 모드여야 하는 UNIX 파이프의 쓰기 끝을 감싸는 파일 유사 객체로부터 단방향 스트림 연결을 생성합니다. 반환되는 트랜스포트는 WriteTransport이며, 읽기 관련 메서드는 없습니다. 반환되는 프로토콜은 BaseProtocol입니다.
  • subprocess_shell(protocol_factory, cmd, <options>): 플랫폼의 “셸” 구문을 사용하는 문자열인 cmd로부터 서브프로세스를 생성합니다. 이는 shell=True로 호출한 표준 라이브러리의 subprocess.Popen()클래스와 유사합니다. 나머지 인자와 반환값은 아래에 설명되어 있습니다.
  • subprocess_exec(protocol_factory, *args, <options>): 하나 이상의 문자열 인자로부터 서브프로세스를 생성합니다. 여기서 첫 번째 문자열은 실행할 프로그램을 지정하고, 나머지 문자열은 프로그램의 인자를 지정합니다. (따라서 sys.argv값이 Python 스크립트라고 가정하면 문자열 인자들이 함께 해당 값이 됩니다.) 이는 shell=False로 호출하고 문자열 목록을 첫 번째 인자로 전달한 표준 라이브러리의 subprocess.Popen()클래스와 유사합니다. 그러나 Popen()은 문자열 목록인 단일 인자를 받는 반면, subprocess_exec()은 여러 문자열 인자를 받습니다. 나머지 인자와 반환값은 아래에 설명되어 있습니다.

실행할 프로그램을 지정하는 방식을 제외하면 두 subprocess_*()메서드는 동일하게 동작합니다. 반환되는 트랜스포트는 일반적인 양방향 스트림 트랜스포트와 다른 인터페이스를 가진 SubprocessTransport입니다. 반환되는 프로토콜은 사용자 지정 인터페이스도 가진 SubprocessProtocol입니다.

<options>는 모두 선택적 키워드 인자를 사용하여 지정합니다.

  • stdin: connect_write_pipe()를 사용하여 서브프로세스의 표준 입력 스트림에 연결할 파이프를 나타내는 파일 유사 객체이거나, 상수 subprocess.PIPE(기본값)입니다. 기본적으로 새 파이프가 생성되고 연결됩니다.
  • stdout: connect_read_pipe()를 사용하여 서브프로세스의 표준 출력 스트림에 연결할 파이프를 나타내는 파일 유사 객체이거나, 상수 subprocess.PIPE(기본값)입니다. 기본적으로 새 파이프가 생성되고 연결됩니다.
  • stderr: connect_read_pipe()를 사용하여 서브프로세스의 표준 오류 스트림에 연결할 파이프를 나타내는 파일과 유사한 객체이거나, 상수 subprocess.PIPE (기본값) 또는 subprocess.STDOUT 중 하나입니다. 기본적으로 새 파이프가 생성되고 연결됩니다. subprocess.STDOUT이 지정되면 서브프로세스의 표준 오류 스트림이 표준 출력 스트림과 동일한 파이프에 연결됩니다.
  • bufsize: 파이프를 생성할 때 사용할 버퍼 크기이며, subprocess.Popen()에 전달됩니다. 기본 구현에서는 기본값이 0이며, Windows에서는 0이어야 합니다. 이러한 기본값은 subprocess.Popen()의 기본값과 다릅니다.
  • executable, preexec_fn, close_fds, cwd, env, startupinfo, creationflags, restore_signals, start_new_session, pass_fds: 이러한 선택적 인자는 해석하지 않고 subprocess.Popen()에 전달됩니다.

시그널 콜백

이러한 메서드는 UNIX에서만 지원됩니다.

  • add_signal_handler(sig, callback, *args)메서드입니다. 시그널 sig를 수신할 때마다 callback(*args)가 호출되도록 설정합니다. 동일한 시그널에 다른 콜백을 지정하면 이전 핸들러가 대체됩니다(시그널당 하나의 핸들러만 활성화될 수 있습니다). sigsignal 모듈에 정의된 유효한 시그널 번호여야 합니다. 시그널을 처리할 수 없으면 예외가 발생합니다. 유효한 시그널이 아니거나 포착할 수 없는 시그널(예: SIGKILL)인 경우에는 ValueError가 발생하고, 이 특정 이벤트 루프 인스턴스가 시그널을 처리할 수 없는 경우에는 RuntimeError가 발생합니다(시그널은 프로세스별로 전역이므로 주 스레드에 연결된 이벤트 루프만 시그널을 처리할 수 있습니다).
  • remove_signal_handler(sig)메서드입니다. 설정된 경우 시그널 sig의 핸들러를 제거합니다. add_signal_handler()와 동일한 예외를 발생시킵니다(단, 포착할 수 없는 시그널의 경우 RuntimeError를 발생시키는 대신 False를 반환할 수 있습니다). 핸들러가 성공적으로 제거되면 True를 반환하고, 설정된 핸들러가 없으면 False를 반환합니다.

참고: 이러한 메서드가 정적으로 지원되지 않는 것으로 알려진 경우 RuntimeError대신 NotImplementedError를 발생시킬 수 있습니다.

콜백의 상호 배제

이벤트 루프는 콜백의 상호 배제를 보장해야 합니다. 즉, 이전 콜백이 아직 실행 중인 동안에는 콜백을 시작해서는 안 됩니다. 이는 모든 유형의 콜백에 적용되어야 하며, call_soon(), call_later(), call_at(), call_soon_threadsafe(), add_reader(), add_writer() 또는 add_signal_handler()를 사용하여 예약되었는지와 관계없이 적용되어야 합니다.

예외

Python의 예외에는 두 가지 범주가 있습니다. Exception 클래스에서 파생되는 예외와 BaseException에서 파생되는 예외입니다. Exception에서 파생되는 예외는 일반적으로 적절하게 포착되고 처리됩니다. 예를 들어 Future를 통해 전달되며, 콜백에서 발생하면 기록되고 무시됩니다.

그러나 BaseException에서만 파생되는 예외는 일반적으로 포착되지 않으며, 대개 트레이스백과 함께 프로그램이 종료됩니다. 일부 경우에는 이러한 예외가 포착된 후 다시 발생합니다. (이 범주의 예로는 KeyboardInterruptSystemExit가 있으며, 일반적으로 이러한 예외를 대부분의 다른 예외와 동일하게 처리하는 것은 현명하지 않습니다.)

이벤트 루프는 후자의 범주를 해당 exception handler에 전달합니다. 이는 매개변수로 context 딕셔너리를 받는 콜백입니다.:

def exception_handler(context):
    ...

context에는 여러 가지 키가 있을 수 있지만, 그중 몇 가지는 매우 널리 사용됩니다.

  • 'message': 오류 메시지입니다.
  • 'exception': 예외 인스턴스이며, 예외가 없으면 None입니다.
  • 'source_traceback': 오류와 관련된 객체가 생성된 시점의 스택을 나타내는 문자열 목록입니다.
  • 'handle_traceback': 오류에 관련된 핸들이 생성된 시점의 스택을 나타내는 문자열 목록입니다.

루프에는 예외 처리와 관련된 다음 메서드가 있습니다.

  • get_exception_handler()는 루프에 등록된 현재 예외 처리기를 반환합니다.
  • set_exception_handler(handler)는 예외 처리기를 설정합니다.
  • default_exception_handler(context)는 이 루프 구현의 기본 예외 처리기입니다.
  • call_exception_handler(context)context를 등록된 예외 처리기에 전달합니다. 이를 통해 서드파티 라이브러리가 처리되지 않은 예외를 일관되게 처리할 수 있습니다.

    명시적인 set_exception_handler() 호출로 기본 처리기를 재정의하지 않은 경우, 루프는 default_exception_handler()를 사용합니다.

디버그 모드

기본적으로 루프는 릴리스 모드로 작동합니다. 애플리케이션은 일부 성능을 희생하여 더 나은 오류 보고를 위해 디버그 모드를 활성화할 수 있습니다.

디버그 모드에서는 다음과 같은 여러 추가 검사가 활성화됩니다.

  • 퓨처와 태스크에서 처리되지 않은 예외가 발생한 경우 소스 트레이스백을 사용할 수 있습니다.
  • 루프는 I/O에서 의도치 않은 블로킹을 감지하기 위해 느린 콜백을 검사합니다.

    loop.slow_callback_duration특성은 느린 콜백이 보고되기 전에 두 yield points 사이에서 허용되는 최대 실행 시간을 제어합니다. 기본값은 0.1초이며, 이 값을 할당하여 변경할 수 있습니다.

디버그 모드와 관련된 메서드는 두 가지입니다.

  • get_debug()디버그 모드가 활성화되어 있으면 True를 반환하고, 그렇지 않으면 False를 반환합니다.
  • set_debug(enabled)는 인자가 True이면 디버그 모드를 활성화합니다.

PYTHONASYNCIODEBUG 환경 변수가 정의되어 있고 비어 있지 않으면 디버그 모드가 자동으로 활성화됩니다.

핸들

일회성 콜백을 등록하는 다양한 메서드(call_soon(), call_later(), call_at()call_soon_threadsafe())는 모두 콜백을 취소하는 데 사용할 수 있는 등록을 나타내는 객체를 반환합니다. 이 객체를 Handle이라고 합니다. 핸들은 불투명하며 공개 메서드는 하나뿐입니다.

  • cancel(): 콜백을 취소합니다.

add_reader(), add_writer()add_signal_handler()는 핸들을 반환하지 않는다는 점에 유의하십시오.

서버

create_server() 메서드는 요청을 수락하는 데 사용되는 소켓(또는 기타 네트워크 객체)을 감싸는 Server 인스턴스를 반환합니다. 이 클래스에는 공개 메서드가 두 가지 있습니다.

  • close(): 서비스를 닫습니다. 그러면 새 요청의 수락은 중지되지만, 이미 수락되어 현재 처리 중인 요청은 취소되지 않습니다.
  • wait_closed(): 서비스가 닫히고 수락된 모든 요청이 처리될 때까지 차단하는 코루틴입니다.

퓨처

여기의 asyncio.Future 클래스는 PEP 3148에서 지정한 concurrent.futures.Future 클래스와 의도적으로 유사하지만, 약간의 차이가 있습니다. 이 PEP에서 Future 또는 futures를 언급할 때마다 concurrent.futures.Future 가 명시적으로 언급되지 않는 한 asyncio.Future를 가리키는 것으로 이해해야 합니다. 지원되는 공개 API는 다음과 같으며, PEP 3148과의 차이를 나타냅니다.

  • cancel(). 퓨처가 이미 완료되었거나 취소된 경우 아무 작업도 하지 않고 False를 반환합니다. 그렇지 않으면 퓨처를 취소하려고 시도하고 True를 반환합니다. 취소 시도가 성공하면 결국 퓨처의 상태가 취소됨으로 변경되어 cancelled()True를 반환하게 되며, 콜백이 예약됩니다. 일반 퓨처의 경우 취소는 항상 즉시 성공하지만, 태스크의 경우(아래 참조) 태스크가 취소 시도를 무시하거나 지연할 수 있습니다.
  • cancelled(). 퓨처가 성공적으로 취소되었으면 True를 반환합니다.
  • done(). 퓨처가 완료되었으면 True를 반환합니다. 취소된 퓨처도 완료된 것으로 간주된다는 점에 유의하십시오(여기와 모든 곳에서 동일합니다).
  • result(). set_result()로 설정된 결과를 반환하거나, set_exception()로 설정된 예외를 발생시킵니다. 취소된 경우 CancelledError를 발생시킵니다. 관련 PEP 3148과의 차이점: 타임아웃 인자가 없으며 대기하지 않습니다. 퓨처가 아직 완료되지 않았다면 예외를 발생시킵니다.
  • exception(). set_exception()로 설정된 예외를 반환하거나, set_result()로 결과가 설정된 경우 None을 반환합니다. 취소된 경우 CancelledError를 발생시킵니다. 관련 PEP 3148과의 차이점: 타임아웃 인자가 없으며 대기하지 않습니다. 퓨처가 아직 완료되지 않았다면 예외를 발생시킵니다.
  • add_done_callback(fn). 퓨처가 완료되거나 취소될 때 실행할 콜백을 추가합니다. 퓨처가 이미 완료되었거나 취소된 경우 call_soon()을 사용하여 콜백이 실행되도록 예약합니다. 관련 PEP 3148과의 차이점: 콜백은 절대 즉시 호출되지 않으며 항상 호출자의 컨텍스트에서 호출됩니다. 일반적으로 이 컨텍스트는 스레드입니다. 이를 call_soon()을 통해 콜백을 호출하는 것으로 생각할 수 있습니다. 관련 PEP 3148과 일치시키기 위해, 이 콜백은 이 PEP에서 정의된 다른 모든 콜백과 달리, 그리고 아래의 “콜백 스타일” 절에 있는 규칙을 무시하고, 항상 단일 인자인 Future 객체와 함께 호출된다는 점에 유의하십시오. (call_soon()으로 예약된 콜백을 엄격하게 직렬화하는 이유는 여기에도 적용됩니다.)
  • remove_done_callback(fn)입니다. 콜백 목록에서 인자를 제거합니다. 이 메서드는 PEP 3148에 정의되어 있지 않습니다. 인자는 ==를 사용하여 add_done_callback()에 전달된 인자와 같아야 합니다. 콜백이 제거된 횟수를 반환합니다.
  • set_result(result)입니다. Future는 이미 완료되었거나 취소된 상태가 아니어야 합니다. 이렇게 하면 Future가 완료되고 콜백이 예약됩니다. 관련 PEP 3148과의 차이점: 이는 공개 API입니다.
  • set_exception(exception)입니다. Future는 이미 완료되었거나 취소된 상태가 아니어야 합니다. 이렇게 하면 Future가 완료되고 콜백이 예약됩니다. 관련 PEP 3148과의 차이점: 이는 공개 API입니다.

내부 메서드 set_running_or_notify_cancel()은 지원되지 않습니다. 실행 상태를 설정할 방법이 없습니다. 마찬가지로 메서드 running()도 지원되지 않습니다.

다음 예외가 정의되어 있습니다.

  • InvalidStateError입니다. Future가 호출되는 메서드에서 허용하는 상태가 아닐 때마다 발생합니다(예: 이미 완료된 Future에서 set_result()를 호출하거나 아직 완료되지 않은 Future에서 result()를 호출하는 경우).
  • InvalidTimeoutError입니다. 0이 아닌 timeout인자가 주어질 때 result()exception()에서 발생합니다.
  • CancelledError입니다. concurrent.futures.CancelledError의 별칭입니다. 취소된 Future에서 result()또는 exception()을 호출할 때 발생합니다.
  • TimeoutError입니다. concurrent.futures.TimeoutError의 별칭입니다. run_until_complete()에서 발생할 수 있습니다.

Future는 생성될 때 이벤트 루프와 연결됩니다.

asyncio.Future객체는 concurrent.futures 패키지의 wait()as_completed()함수에 허용되지 않습니다. 그러나 아래에 설명된 것과 유사한 API인 asyncio.wait()asyncio.as_completed()가 있습니다.

asyncio.Future객체는 코루틴에서 사용될 때 yield from표현식에 허용됩니다. 이는 Future의 __iter__()인터페이스를 통해 구현됩니다. 아래의 “Coroutines and the Scheduler” 절을 참조하십시오.

Future가 가비지 컬렉션될 때 연결된 예외가 있지만 result()exception()도 호출된 적이 없다면 예외가 로그에 기록됩니다. (코루틴이 yield from을 사용하여 Future를 기다리는 경우, 코루틴이 재개되면 해당 Future의 result() 메서드가 호출됩니다.)

앞으로(말장난을 하자면) asyncio.Futureconcurrent.futures.Future를 통합할 수도 있습니다. 예를 들어 후자에 yield from과 함께 작동하는 __iter__() 메서드를 추가하는 방식입니다. 아직 완료되지 않은 Future에서 예를 들어 result()를 호출하여 이벤트 루프를 실수로 블로킹하는 것을 방지하기 위해, 블로킹 작업이 현재 스레드에서 이벤트 루프가 활성 상태인지 감지하고 대신 예외를 발생시킬 수 있습니다. 그러나 현재 PEP는 Python 3.3을 넘어서는 종속성을 두지 않으려고 하므로, 현재로서는 concurrent.futures.Future에 대한 변경은 고려 대상에서 제외됩니다.

Future와 관련된 공개 함수가 몇 가지 있습니다.

  • asyncio.async(arg). 이 함수는 코루틴 객체 또는 Future(즉, yield from을 사용하여 함께 사용할 수 있는 모든 것)인 인자를 받아 Future를 반환합니다. 인자가 Future이면 변경하지 않고 반환하며, 코루틴 객체이면 이를 Task로 감쌉니다(TaskFuture의 서브클래스임을 기억하십시오).
  • asyncio.wrap_future(future). 이 함수는 PEP 3148 Future(즉, concurrent.futures.Future의 인스턴스)를 받아 이벤트 루프와 호환되는 Future(즉, asyncio.Future 인스턴스)를 반환합니다.

트랜스포트

트랜스포트와 프로토콜은 Twisted와 PEP의 강한 영향을 받습니다. 3153. 사용자가 트랜스포트를 직접 구현하거나 인스턴스화하는 경우는 드뭅니다. 대신, 이벤트 루프가 트랜스포트를 설정하기 위한 유틸리티 메서드를 제공합니다.

트랜스포트는 프로토콜과 함께 작동합니다. 프로토콜은 일반적으로 사용되는 트랜스포트의 정확한 유형을 알거나 신경 쓰지 않고 작성되며, 트랜스포트는 매우 다양한 프로토콜과 함께 사용할 수 있습니다. 예를 들어 HTTP 클라이언트 프로토콜 구현은 일반 소켓 트랜스포트나 SSL/TLS 트랜스포트와 함께 사용할 수 있습니다. 일반 소켓 트랜스포트는 HTTP 외에도 여러 프로토콜(예: SMTP, IMAP, POP, FTP, IRC, SPDY)과 함께 사용할 수 있습니다.

가장 일반적인 트랜스포트 유형은 양방향 스트림 트랜스포트입니다. 단방향 스트림 트랜스포트(파이프에 사용됨)와 데이터그램 트랜스포트(create_datagram_endpoint() 메서드에서 사용됨)도 있습니다.

모든 트랜스포트에 대한 메서드

  • get_extra_info(name, default=None). 이는 트랜스포트에 관한 구현별 정보를 반환하는 만능 메서드입니다. 첫 번째 인자는 검색할 추가 필드의 이름입니다. 선택적 두 번째 인자는 반환할 기본값입니다. 지원되는 추가 필드 이름을 확인하려면 구현 문서를 참조하십시오. 지원되지 않는 이름에 대해서는 항상 기본값이 반환됩니다.

양방향 스트림 트랜스포트

양방향 스트림 트랜스포트는 소켓 또는 이와 유사한 것(예를 들어 UNIX 파이프 쌍이나 SSL/TLS 연결) 위에 구축된 추상화입니다.

대부분의 연결은 비대칭적인 특성을 가집니다. 클라이언트와 서버는 일반적으로 역할과 동작이 매우 다릅니다. 따라서 전송 계층과 프로토콜 사이의 인터페이스도 비대칭적입니다. 프로토콜의 관점에서 데이터를 작성하는 작업은 전송 객체의 write() 메서드를 호출하여 수행합니다. 이 메서드는 데이터를 버퍼링하고 즉시 반환합니다. 그러나 데이터를 읽는 작업에서는 전송 계층이 더 적극적인 역할을 합니다. 소켓이나 다른 데이터 소스에서 일부 데이터가 읽힐 때마다 전송 계층은 프로토콜의 data_received() 메서드를 호출합니다.

그럼에도 양방향 스트림에서 사용하는 전송 계층과 프로토콜 사이의 인터페이스는 클라이언트와 서버에서 동일합니다. 클라이언트와 서버 사이의 연결은 본질적으로 각 방향으로 하나씩 존재하는 스트림 쌍이기 때문입니다.

양방향 스트림 전송에는 다음과 같은 공개 메서드가 있습니다.

  • write(data). 일부 바이트를 작성하십시오. 인자는 bytes 객체여야 합니다. None을 반환합니다. 전송 계층은 바이트를 자유롭게 버퍼링할 수 있지만, 결국 바이트가 반대편의 엔터티로 전송되도록 해야 하며 스트림 동작을 유지해야 합니다. 즉, t.write(b'abc'); t.write(b'def')t.write(b'abcdef')와 동등하며, 다음과도 동등합니다.:
    t.write(b'a')
    t.write(b'b')
    t.write(b'c')
    t.write(b'd')
    t.write(b'e')
    t.write(b'f')
    
  • writelines(iterable). 다음과 동등합니다.:
    for data in iterable:
        self.write(data)
    
  • write_eof(). 연결의 쓰기 끝을 닫으십시오. 이후에는 write()를 호출할 수 없습니다. 버퍼링된 모든 데이터가 전송되면 전송 계층은 더 이상 데이터가 수신되지 않는다는 신호를 반대편에 보냅니다. 일부 프로토콜은 이 작업을 지원하지 않습니다. 이러한 경우 write_eof()를 호출하면 예외가 발생합니다. (참고: 이전에는 half_close()라고 불렀지만, 그 용도를 이미 알고 있지 않다면 이 이름만으로는 어느 쪽이 닫히는지 알 수 없습니다.)
  • can_write_eof(). 프로토콜이 write_eof()를 지원하면 True를 반환하고, 지원하지 않으면 False를 반환합니다. (이 메서드는 일반적으로 Transport 클래스의 상태가 아니라 특정 Transport 클래스에만 의존하는 고정된 값을 반환합니다. 일부 프로토콜은 write_eof()를 사용할 수 없을 때 동작을 변경해야 하므로 이 메서드가 필요합니다. 예를 들어 HTTP에서 크기를 미리 알 수 없는 데이터를 전송할 때는 일반적으로 write_eof()를 사용하여 데이터의 끝을 나타냅니다. 그러나 SSL/TLS는 이를 지원하지 않으므로, 이 경우 HTTP 프로토콜 구현은 “chunked” 전송 인코딩을 사용해야 합니다. 그러나 데이터 크기를 미리 알고 있다면 두 경우 모두 가장 좋은 방법은 Content-Length 헤더를 사용하는 것입니다.)
  • get_write_buffer_size(). 전송 계층의 현재 쓰기 버퍼 크기를 바이트 단위로 반환합니다. 이 메서드는 전송 계층이 명시적으로 관리하는 쓰기 버퍼만 파악합니다. 네트워크 스택의 다른 계층이나 네트워크의 다른 위치에서 수행되는 버퍼링은 보고되지 않습니다.
  • set_write_buffer_limits(high=None, low=None). 흐름 제어를 위한 상한 및 하한 워터마크를 설정하십시오.

    이 두 값은 프로토콜의 pause_writing()resume_writing() 메서드를 호출할 시점을 제어합니다. 지정하는 경우 하한 워터마크는 상한 워터마크보다 작거나 같아야 합니다. 어느 값도 음수일 수 없습니다.

    기본값은 구현에 따라 다릅니다. high-water 제한만 지정된 경우, low-water 제한은 high-water 제한보다 작거나 같은 구현별 값으로 기본 설정됩니다. high를 0으로 설정하면 low도 0으로 강제되며, 버퍼가 비어 있지 않게 될 때마다 pause_writing()이 호출됩니다. low를 0으로 설정하면 버퍼가 비어 있을 때만 resume_writing()이 호출됩니다. 어느 한 제한에든 0을 사용하는 것은 일반적으로 최적이 아닙니다. I/O와 계산을 동시에 수행할 기회가 줄어들기 때문입니다.

  • pause_reading(). 이후 resume_reading()호출이 이루어질 때까지 프로토콜로의 데이터 전달을 일시 중지합니다. pause_reading()resume_reading()사이에는 프로토콜의 data_received()메서드가 호출되지 않습니다.
  • resume_reading(). data_received()을 통해 프로토콜로의 데이터 전달을 다시 시작합니다. “paused”는 이진 상태라는 점에 유의하십시오. 전송이 일시 중지되지 않은 경우에만 pause_reading()을 호출해야 하며, 전송이 일시 중지된 경우에만 resume_reading()을 호출해야 합니다.
  • close(). 반대편 엔터티와의 연결을 끊습니다. write()로 버퍼링된 모든 데이터는 연결이 실제로 닫히기 전에 (결국) 전송됩니다. 프로토콜의 data_received()메서드는 다시 호출되지 않습니다. 버퍼링된 모든 데이터가 플러시되면 프로토콜의 connection_lost()메서드가 None을 인자로 하여 호출됩니다. 이 메서드는 이 모든 과정이 완료될 때까지 기다리지 않는다는 점에 유의하십시오.
  • abort(). 연결을 즉시 끊습니다. 전송에 여전히 버퍼링된 데이터는 폐기됩니다. 곧 프로토콜의 connection_lost()메서드가 None을 인자로 하여 호출됩니다.

단방향 스트림 전송

쓰기 스트림 전송은 양방향 스트림 전송에 설명된 write(), writelines(), write_eof(), can_write_eof(), close()abort() 메서드를 지원합니다.

읽기 스트림 전송은 양방향 스트림 전송에 설명된 pause_reading(), resume_reading()close() 메서드를 지원합니다.

쓰기 스트림 전송은 연결된 프로토콜에서 connection_made()connection_lost() 만 호출합니다.

읽기 스트림 전송은 아래 프로토콜 섹션에 지정된 모든 프로토콜 메서드(즉, 앞의 두 메서드와 data_received()eof_received())를 호출할 수 있습니다.

데이터그램 전송

데이터그램 전송에는 다음 메서드가 있습니다:

  • sendto(data, addr=None). 데이터그램(바이트 객체)을 보냅니다. 선택적 두 번째 인자는 대상 주소입니다. 생략된 경우, 이 전송을 생성한 create_datagram_endpoint() 호출에서 remote_addr가 지정되어 있어야 합니다. 지정된 경우 remote_addr도 지정되어 있었다면 두 주소가 일치해야 합니다. (data, addr) 쌍은 즉시 전송되거나 버퍼링될 수 있습니다. 반환값은 None입니다.
  • abort(). 전송을 즉시 닫습니다. 버퍼링된 데이터는 폐기됩니다.
  • close(). 전송을 닫습니다. 버퍼링된 데이터는 비동기적으로 전송됩니다.

데이터그램 전송은 연결된 프로토콜 객체에서 다음 메서드를 호출합니다: connection_made(), connection_lost(), error_received()datagram_received()입니다. (이 메서드 이름에서 “Connection”은 약간 부정확한 명칭이지만 개념은 여전히 존재합니다. connection_made()는 엔드포인트를 나타내는 전송이 생성되었음을 의미하고, connection_lost()는 전송이 닫혔음을 의미합니다.)

서브프로세스 전송

서브프로세스 전송에는 다음 메서드가 있습니다.

  • get_pid(). 서브프로세스의 프로세스 ID를 반환합니다.
  • get_returncode(). 프로세스가 종료된 경우 프로세스 반환 코드를 반환하고, 그렇지 않으면 None을 반환합니다.
  • get_pipe_transport(fd). 인자에 해당하는 파이프 전송(단방향 스트림 전송)을 반환합니다. 인자는 서브프로세스의 stdin, stdout 또는 stderr를 나타내는 0, 1 또는 2여야 합니다. 그러한 파이프 전송이 없으면 None을 반환합니다. stdin의 경우 이는 쓰기 전송이고, stdout 및 stderr의 경우 읽기 전송입니다. 서브프로세스의 stdin에 쓰는 데 사용할 수 있는 전송을 얻으려면 이 메서드를 사용해야 합니다.
  • send_signal(signal). 서브프로세스에 신호를 보냅니다.
  • terminate(). 서브프로세스를 종료합니다.
  • kill(). 서브프로세스를 강제 종료합니다. Windows에서는 terminate()의 별칭입니다.
  • close()입니다. 이는 terminate()의 별칭입니다.

send_signal(), terminate()kill()은 표준 라이브러리 subprocess 모듈의 해당 메서드를 감쌉니다.

프로토콜

프로토콜은 항상 전송과 함께 사용됩니다. 몇 가지 일반적인 프로토콜(예를 들어 괜찮지만 반드시 뛰어나지는 않은 HTTP 클라이언트 및 서버 구현)이 제공되지만, 대부분의 프로토콜은 사용자 코드나 서드 파티 라이브러리로 구현됩니다.

전송과 마찬가지로 스트림 프로토콜, 데이터그램 프로토콜, 그리고 아마도 기타 사용자 지정 프로토콜을 구분합니다. 가장 일반적인 프로토콜 유형은 양방향 스트림 프로토콜입니다. (단방향 프로토콜은 없습니다.)

스트림 프로토콜

(양방향) 스트림 프로토콜은 다음 메서드를 구현해야 하며, 전송이 이 메서드들을 호출합니다. 이를 항상 이벤트 루프가 올바른 컨텍스트에서 호출하는 콜백이라고 생각하십시오. (훨씬 위에 있는 “Context” 섹션을 참조하십시오.)

  • connection_made(transport). 전송이 준비되었으며 반대편의 엔터티에 연결되었음을 나타냅니다. 프로토콜은 나중에 write()와 기타 메서드를 호출할 수 있도록 전송 참조를 인스턴스 변수로 저장하는 것이 좋으며, 이 시점에 초기 인사말이나 요청을 작성할 수도 있습니다.
  • data_received(data). 전송이 연결에서 일부 바이트를 읽었습니다. 인자는 항상 비어 있지 않은 bytes 객체입니다. 이 방식으로 전달되는 데이터의 최소 또는 최대 크기는 보장되지 않습니다. p.data_received(b'abcdef')은 다음과 정확히 동일하게 취급해야 합니다.:
    p.data_received(b'abc')
    p.data_received(b'def')
    
  • eof_received(). 상대편이 write_eof() (또는 이에 상응하는 작업)를 호출했을 때 호출됩니다. 이 메서드가 None을 포함한 거짓 값을 반환하면 전송이 스스로 닫힙니다. 참 값을 반환하면 전송을 닫는 것은 프로토콜에 달려 있습니다. 그러나 SSL/TLS 연결에서는 이를 무시합니다. TLS 표준은 더 이상 데이터가 전송되지 않아야 하며 “closure alert”를 받는 즉시 연결을 닫도록 요구하기 때문입니다.

    기본 구현은 None을 반환합니다.

  • pause_writing(). 프로토콜에 전송으로의 데이터 쓰기를 일시적으로 중지하도록 요청합니다. 요청을 따를지는 선택 사항이지만, 계속 쓰면 전송의 버퍼가 제한 없이 증가할 수 있습니다. 이 메서드가 호출되는 버퍼 크기는 전송의 set_write_buffer_limits()메서드를 통해 제어할 수 있습니다.
  • resume_writing(). 프로토콜에 전송으로의 데이터 쓰기를 다시 시작해도 안전하다고 알립니다. 이는 call_soon()을 사용하여 간접적으로 호출되는 것이 아니라 전송의 write()메서드에 의해 직접 호출될 수 있으므로, write()가 반환된 직후 프로토콜이 일시 중지된 상태를 인식할 수 있다는 점에 유의하십시오.
  • connection_lost(exc). 트랜스포트가 닫혔거나 중단되었거나, 상대편이 연결을 정상적으로 닫은 것을 감지했거나, 예상치 못한 오류를 만났습니다. 처음 세 경우에는 인자가 None이며, 예상치 못한 오류의 경우에는 트랜스포트가 작업을 포기하게 만든 예외가 인자입니다.

다음은 기본 호출의 순서와 횟수를 나타내는 표입니다.

  1. connection_made() – 정확히 한 번 호출됩니다.
  2. data_received() – 0회 이상 호출됩니다.
  3. eof_received() – 최대 한 번 호출됩니다.
  4. connection_lost() – 정확히 한 번 호출됩니다.

pause_writing()resume_writing() 호출은 쌍으로 발생하며 #1과 #4 사이에서만 발생합니다. 이러한 쌍은 중첩되지 않습니다. 마지막 resume_writing() 호출은 생략될 수 있습니다. 즉, 일시 중지된 연결이 손실되어 다시 재개되지 않을 수 있습니다.

데이터그램 프로토콜

데이터그램 프로토콜에는 스트림 프로토콜과 동일한 시그니처를 가진 connection_made()connection_lost() 메서드가 있습니다. (데이터그램 트랜스포트에 관한 절에서 설명했듯이, 소켓의 개방과 폐쇄를 나타내는 서로 다른 메서드 이름을 정의하기보다 다소 특이한 명명법을 선호합니다.)

또한 다음 메서드가 있습니다.

  • datagram_received(data, addr). 데이터그램 data (바이트 객체)가 원격 주소 addr(IPv4 2-튜플 또는 IPv6 4-튜플)에서 수신되었음을 나타냅니다.
  • error_received(exc). 송신 또는 수신 작업에서 OSError 예외가 발생했음을 나타냅니다. 데이터그램 오류는 일시적일 수 있으므로 엔드포인트를 닫으려는 경우 트랜스포트의 close() 메서드를 호출할지는 프로토콜이 결정합니다.

다음은 호출의 순서와 횟수를 나타내는 도표입니다.

  1. connection_made() – 정확히 한 번 호출됩니다.
  2. datagram_received(), error_received() – 0회 이상 호출됩니다.
  3. connection_lost() – 정확히 한 번 호출됩니다.

서브프로세스 프로토콜

서브프로세스 프로토콜에는 스트림 프로토콜과 동일한 시그니처를 가진 connection_made(), connection_lost(), pause_writing()resume_writing() 메서드가 있습니다. 또한 다음 메서드가 있습니다.

  • pipe_data_received(fd, data). 서브프로세스가 표준 출력 또는 표준 오류에 데이터를 기록할 때 호출됩니다. fd는 파일 디스크립터입니다(표준 출력은 1, 표준 오류는 2). databytes 객체입니다.
  • pipe_connection_lost(fd, exc). 서브프로세스가 표준 입력, 표준 출력 또는 표준 오류를 닫을 때 호출됩니다. fd는 파일 디스크립터입니다. exc는 예외이거나 None입니다.
  • process_exited()입니다. 서브프로세스가 종료되었을 때 호출됩니다. 종료 상태를 가져오려면 트랜스포트의 get_returncode() 메서드를 사용하십시오.

서브프로세스의 동작에 따라 process_exited()pipe_connection_lost()보다 먼저 또는 나중에 호출될 수 있다는 점에 유의하십시오. 예를 들어 서브프로세스가 자신의 stdin/stdout/stderr를 공유하는 하위 서브프로세스를 생성한 후 스스로 종료하면, 모든 파이프가 여전히 열린 상태에서 process_exited()가 호출될 수 있습니다. 반면 서브프로세스가 stdin/stdout/stderr를 닫지만 종료하지 않는 경우에는 process_exited()가 호출되지 않고 세 파이프 모두에 대해 pipe_connection_lost()가 호출될 수 있습니다. (더 일반적인 경우처럼) 서브프로세스가 종료되어 모든 파이프를 암묵적으로 닫는 경우 호출 순서는 정의되지 않습니다.

콜백 스타일

콜백을 받는 대부분의 인터페이스는 위치 인자도 받습니다. 예를 들어 foo("abc", 42)가 곧 호출되도록 하려면 loop.call_soon(foo, "abc", 42)를 호출하십시오. foo() 호출을 예약하려면 loop.call_soon(foo)를 사용하십시오. 이 규약은 일반적인 콜백 프로그래밍에서 필요한 작은 람다의 수를 크게 줄여 줍니다.

이 규칙은 특히 키워드 인자를 지원하지 않습니다. 키워드 인자는 콜백에 선택적인 추가 정보를 전달하는 데 사용됩니다. 따라서 어느 키워드가 어딘가의 피호출자에게 중요한 의미를 가질 수 있는지 걱정하지 않고도 API를 원활하게 발전시킬 수 있습니다. 키워드 인자와 함께 반드시 호출되어야 하는 콜백이 있다면 람다를 사용할 수 있습니다. 예를 들면 다음과 같습니다.:

loop.call_soon(lambda: foo('abc', repeat=42))

코루틴과 스케줄러

이 섹션은 이벤트 루프 인터페이스와 그 상태가 다르기 때문에 별도의 최상위 섹션으로 구성되어 있습니다. 코루틴 사용은 선택 사항이며 콜백만 사용하여 코드를 작성해도 전혀 문제가 없습니다. 반면 스케줄러/코루틴 API의 구현은 하나뿐이며, 코루틴을 사용한다면 바로 그 구현을 사용하게 됩니다.

코루틴

코루틴은 특정 규약을 따르는 제너레이터입니다. 문서화 목적상 모든 코루틴은 @asyncio.coroutine으로 데코레이트해야 하지만, 이를 엄격하게 강제할 수는 없습니다.

코루틴은 원래의 yield 구문 대신 PEP 380에서 도입된 yield from 구문을 사용합니다.

“코루틴”이라는 단어는 “제너레이터”라는 단어와 마찬가지로 서로 다르지만 연관된 두 가지 개념에 사용됩니다.

  • 코루틴을 정의하는 함수(asyncio.coroutine으로 데코레이트된 함수 정의)입니다. 구분이 필요한 경우 이를 코루틴 함수라고 부릅니다.
  • 코루틴 함수를 호출하여 얻는 객체입니다. 이 객체는 결국 완료될 계산 또는 I/O 작업(대개 이 둘의 조합)을 나타냅니다. 구분이 필요한 경우 이를 코루틴 객체라고 부릅니다.

코루틴으로 수행할 수 있는 작업은 다음과 같습니다.

  • result = yield from future – future가 완료될 때까지 코루틴을 일시 중단한 다음, future의 결과를 반환하거나 전파될 예외를 발생시킵니다. (future가 취소되면 CancelledError예외를 발생시킵니다.) 태스크는 Future이며, Future에 대해 설명한 모든 내용이 태스크에도 적용된다는 점에 유의하십시오.
  • result = yield from coroutine – 다른 코루틴이 결과를 생성할 때까지 기다리거나 전파될 예외를 발생시킵니다. coroutine 표현식은 다른 코루틴을 호출하는 호출이어야 합니다.
  • return expressionyield from을 사용하여 이 코루틴을 기다리는 코루틴에 결과를 생성합니다.
  • raise exceptionyield from을 사용하여 이 코루틴을 기다리는 코루틴에서 예외를 발생시킵니다.

코루틴을 호출해도 해당 코드의 실행이 시작되지는 않습니다. 코루틴은 단지 제너레이터일 뿐이며, 호출로 반환되는 코루틴 객체는 실제로 제너레이터 객체이므로 이를 순회할 때까지 아무 작업도 수행하지 않습니다. 코루틴 객체의 경우 실행을 시작하는 기본적인 방법은 두 가지입니다. 다른 코루틴에서 yield from coroutine을 호출하거나(다른 코루틴이 이미 실행 중이라고 가정합니다!), 이를 Task로 변환하는 것입니다(아래 참조).

코루틴과 태스크는 이벤트 루프가 실행 중일 때만 실행될 수 있습니다.

여러 코루틴 기다리기

여러 코루틴 또는 Future를 기다리기 위해 concurrent.futures 패키지의 wait()as_completed() API와 유사한 두 가지 API가 제공됩니다.

  • asyncio.wait(fs, timeout=None, return_when=ALL_COMPLETED)입니다. 이는 fs로 전달된 Future 또는 코루틴이 완료될 때까지 기다리는 코루틴입니다. 코루틴 인자는 Task로 래핑됩니다(아래 참조). 이 함수는 성공할 경우 결과가 두 Future 집합의 튜플인 Future를 반환합니다. (done, pending)에서 done은 완료되었거나 취소된 원래 Future(또는 래핑된 코루틴)의 집합이며, pending은 나머지, 즉 아직 완료되지 않았거나 취소되지 않은 항목의 집합입니다. timeoutreturn_when의 기본값을 사용하는 경우 done은 항상 빈 리스트가 된다는 점에 유의하십시오. 선택적 인자 timeoutreturn_whenconcurrent.futures.wait()의 인자와 동일한 의미와 기본값을 가집니다. timeoutNone이 아니면 전체 작업에 대한 시간 초과를 지정하고, return_when은 중지할 시점을 지정합니다. 상수 FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETEDPEP 3148에서와 동일한 값과 의미로 정의됩니다.
    • ALL_COMPLETED (기본값): 모든 Future가 완료될 때까지 또는 시간 초과가 발생할 때까지 기다립니다.
    • FIRST_COMPLETED: 하나 이상의 Future가 완료될 때까지 또는 시간 초과가 발생할 때까지 기다립니다.
    • FIRST_EXCEPTION: 하나 이상의 Future가 완료되고 예외가 설정되었지만 취소되지는 않을 때까지 기다립니다. (취소된 Future를 조건에서 제외하는 것은 의외일 수 있지만, PEP 3148에서는 이렇게 처리합니다.)
  • asyncio.as_completed(fs, timeout=None)입니다. 값이 Future 또는 코루틴인 이터레이터를 반환합니다. 연속된 값을 기다리면 fs집합에서 다음 Future 또는 코루틴이 완료될 때까지 기다린 후 그 결과를 반환하거나 해당 예외를 발생시킵니다. 선택적 인자 timeoutconcurrent.futures.wait()에서와 동일한 의미와 기본값을 가집니다. 시간 초과가 발생하면 이터레이터가 반환한 다음 Future를 기다릴 때 TimeoutError가 발생합니다. 사용 예:
    for f in as_completed(fs):
        result = yield from f  # May raise an exception.
        # Use result.
    

    이터레이터가 생성한 값을 기다리지 않으면 다른 태스크가 실행되도록 허용하지 않기 때문에 for 루프가 진행되지 않을 수 있습니다.

  • asyncio.wait_for(f, timeout)입니다. 이는 단일 코루틴 또는 Future를 시간 초과와 함께 기다릴 수 있게 하는 편의 기능입니다. 시간 초과가 발생하면 태스크를 취소하고 TimeoutError를 발생시킵니다. 태스크 취소를 피하려면 shield()로 감싸십시오.
  • asyncio.gather(f1, f2, ...)입니다. 모든 인자(Future 또는 코루틴)가 완료될 때까지 기다린 후 해당 결과의 목록을 반환하는 Future를 반환합니다. 하나 이상의 인자가 취소되거나 예외를 발생시키면, 반환된 Future도 취소되거나 예외가 설정되며(첫 번째 인자에 발생한 상황과 일치합니다), 나머지 인자는 백그라운드에서 계속 실행됩니다. 반환된 Future를 취소해도 인자에는 영향을 주지 않습니다. 코루틴 인자는 asyncio.async()를 사용하여 Future로 변환된다는 점에 유의하십시오.
  • asyncio.shield(f)입니다. Future가 취소되지 않도록 보호하면서 기다립니다. 이는 인자와 결과 또는 예외가 정확히 동일한 Future를 반환합니다. 그러나 반환된 Future가 취소되더라도 인자 Future에는 영향을 주지 않습니다.

    이 함수의 사용 사례로는 HTTP 서버에서 요청을 처리하는 코루틴을 위해 쿼리 결과를 캐시하는 코루틴을 들 수 있습니다. 클라이언트가 요청을 취소하면, 클라이언트가 다시 연결했을 때 쿼리 결과가 (바라건대) 캐시되도록 쿼리 캐싱 코루틴을 계속 실행하고 싶을 수도 있습니다. 예를 들어 다음과 같이 작성할 수 있습니다.:

    @asyncio.coroutine
    def handle_request(self, request):
        ...
        cached_query = self.get_cache(...)
        if cached_query is None:
            cached_query = yield from asyncio.shield(self.fill_cache(...))
        ...
    

절전

코루틴 asyncio.sleep(delay)는 지정된 시간 지연 후 반환합니다.

태스크

Task는 독립적으로 실행되는 코루틴을 관리하는 객체입니다. Task 인터페이스는 Future 인터페이스와 동일하며, 실제로 TaskFuture의 서브클래스입니다. 코루틴이 반환되거나 예외를 발생시키면 태스크가 완료됩니다. 결과를 반환하면 해당 결과가 태스크의 결과가 되고, 예외를 발생시키면 해당 예외가 태스크의 예외가 됩니다.

아직 완료되지 않은 태스크를 취소하면 코루틴에 asyncio.CancelledError 예외가 발생합니다. 코루틴이 이 예외를 포착하지 않거나(또는 다시 발생시키면) 태스크는 취소된 것으로 표시됩니다(즉, cancelled()True를 반환합니다). 그러나 코루틴이 어떤 방식으로든 예외를 포착하고 무시하면 계속 실행될 수 있으며(cancelled()False를 반환합니다).

태스크는 코루틴과 Twisted 같은 콜백 기반 프레임워크 간의 상호 운용에도 유용합니다. 코루틴을 Task로 변환한 후에는 해당 Task에 콜백을 추가할 수 있습니다.

코루틴을 태스크로 변환하려면 코루틴 함수를 호출하고 그 결과인 코루틴 객체를 loop.create_task() 메서드에 전달하십시오. 이 용도로 asyncio.ensure_future()를 사용할 수도 있습니다.

모든 코루틴을 태스크로 자동 변환하면 안 되는 이유가 무엇인지 궁금할 수 있습니다. @asyncio.coroutine 데코레이터가 이를 수행할 수 있습니다. 그러나 한 코루틴이 다른 코루틴을 호출하는 경우(이런 호출이 계속 이어지는 경우)에는 이로 인해 속도가 크게 느려집니다. “일반” 코루틴으로 전환하는 오버헤드가 Task로 전환하는 것보다 훨씬 적기 때문입니다.

Task 클래스는 Future에서 파생되며 새로운 메서드를 추가합니다.

  • current_task(loop=None)입니다. 이벤트 루프에서 현재 실행 중인 태스크를 반환하는 클래스 메서드입니다. loopNone이면 이 메서드는 기본 루프의 현재 태스크를 반환합니다. 모든 코루틴은 ensure_future() 또는 loop.create_task()를 사용하여 생성된 Task태스크 컨텍스트 내에서 실행되거나, yield from 또는 await를 사용하여 다른 코루틴에서 호출된 상태로 실행됩니다. 이 메서드는 코루틴 외부에서 호출되면 None을 반환합니다. 예를 들어 loop.call_later()를 사용하여 예약된 콜백에서 호출하는 경우가 그렇습니다.
  • all_tasks(loop=None). 루프의 모든 활성 태스크 집합을 반환하는 클래스 메서드입니다. loopNone인 경우 기본 루프를 사용합니다.

스케줄러

스케줄러에는 공개 인터페이스가 없습니다. yield from futureyield from task를 사용하여 스케줄러와 상호 작용합니다. 실제로 스케줄러를 나타내는 단일 객체는 없습니다 – 스케줄러의 동작은 이벤트 루프의 공개 인터페이스만 사용하여 TaskFuture 클래스에 의해 구현되므로, 타사 이벤트 루프 구현에서도 작동합니다.

편의 유틸리티

FTP나 HTTP와 같은 기본 스트림 기반 클라이언트 및 서버 작성을 간소화하기 위해 몇 가지 함수와 클래스를 제공합니다. 다음과 같습니다:

  • asyncio.open_connection(host, port): EventLoop.create_connection()의 래퍼로, Protocol 팩토리나 클래스를 제공할 필요가 없습니다. (reader, writer)쌍을 반환하는 코루틴이며, 여기서 readerStreamReader의 인스턴스이고 writerStreamWriter의 인스턴스입니다(둘 다 아래에 설명합니다).
  • asyncio.start_server(client_connected_cb, host, port): Protocol 팩토리나 클래스 대신 간단한 콜백 함수를 받는 EventLoop.create_server()의 래퍼입니다. create_server()와 마찬가지로 Server객체를 반환하는 코루틴입니다. 클라이언트 연결이 수락될 때마다 client_connected_cb(reader, writer)가 호출되며, 여기서 readerStreamReader의 인스턴스이고 writerStreamWriter의 인스턴스입니다(둘 다 아래에 설명합니다). client_connected_cb()가 반환한 결과가 코루틴이면 자동으로 Task로 래핑합니다.
  • StreamReader: 읽기 전용 바이너리 스트림과 유사한 인터페이스를 제공하는 클래스이지만, 다양한 읽기 메서드는 코루틴입니다. 일반적으로 StreamReaderProtocol인스턴스에 의해 구동됩니다. 리더는 하나만 존재해야 한다는 점에 유의하십시오. 리더의 인터페이스는 다음과 같습니다:
    • readline(): '\n'로 끝나는 텍스트 한 줄을 나타내는 바이트 문자열을 읽거나, 둘 중 먼저 도달하는 스트림의 끝까지 읽는 코루틴입니다.
    • read(n): 최대 n바이트를 읽는 코루틴입니다. n이 생략되거나 음수이면 스트림의 끝까지 읽습니다.
    • readexactly(n): 정확히 n바이트를 읽거나, 둘 중 먼저 도달하는 스트림의 끝까지 읽는 코루틴입니다.
    • exception(): set_exception()을 사용하여 스트림에 설정된 예외를 반환하거나, 예외가 설정되지 않았으면 None을 반환합니다.

    드라이버의 인터페이스는 다음과 같습니다:

    • feed_data(data): data(bytes 객체)를 내부 버퍼에 추가합니다. 리더의 요구 사항을 충족하기에 충분한 데이터를 제공하면 차단된 읽기 코루틴의 차단을 해제합니다.
    • feed_eof(): 버퍼의 끝을 알립니다. 차단된 읽기 코루틴의 차단을 해제합니다. 이 호출 후에는 리더에 더 이상 데이터를 제공해서는 안 됩니다.
    • set_exception(exc): 스트림에 예외를 설정합니다. 이후의 모든 읽기 메서드는 이 예외를 발생시킵니다. 이 호출 이후에는 리더에 더 이상 데이터를 공급해서는 안 됩니다.
  • StreamWriter: 쓰기 전용 바이너리 스트림과 유사한 인터페이스를 제공하는 클래스입니다. 트랜스포트를 래핑합니다. 인터페이스는 트랜스포트 인터페이스의 확장된 부분 집합입니다. 다음 메서드는 해당 트랜스포트 메서드와 동일하게 동작합니다: write(), writelines(), write_eof(), can_write_eof(), get_extra_info(), close(). 쓰기 메서드가 코루틴이 아니라는 점에 유의하십시오(이는 트랜스포트의 경우와는 같지만 StreamReader 클래스와는 다릅니다). 다음 메서드는 트랜스포트 인터페이스에 추가됩니다.
    • drain(): 흐름 제어를 위해 상당한 양의 데이터를 쓴 후 yield from과 함께 호출해야 합니다. 의도된 사용 방법은 다음과 같습니다.:
      writer.write(data)
      yield from writer.drain()
      

      이 메서드는 엄밀히 말해 코루틴이 아닙니다. Future 또는 빈 튜플을 반환하며, 둘 다 yield from에 전달할 수 있습니다. 이 메서드의 사용은 선택 사항입니다. 그러나 이 메서드를 사용하지 않으면 StreamWriter의 기반이 되는 트랜스포트의 내부 버퍼가 기록기에 기록된 모든 데이터로 가득 찰 수 있습니다. 앱이 작성하는 데이터의 양에 엄격한 제한을 두지 않는다면, 트랜스포트 버퍼가 가득 차지 않도록 때때로 yield from drain()을 호출해야 합니다.

  • StreamReaderProtocol: 양방향 스트림 트랜스포트/프로토콜 인터페이스와 StreamReaderStreamWriter 클래스 사이의 어댑터로 사용되는 프로토콜 구현입니다. 특정 StreamReader 인스턴스의 드라이버로 동작하며, 다양한 프로토콜 콜백에 응답하여 해당 인스턴스의 feed_data(), feed_eof(), set_exception() 메서드를 호출합니다. 또한 StreamWriter 인스턴스의 drain() 메서드 동작을 제어합니다.

동기화

threading 모듈의 잠금, 이벤트, 조건 및 세마포어를 모델로 한 구현이 제공되며 asyncio.locks 하위 모듈을 임포트하여 액세스할 수 있습니다. queue 모듈의 큐를 모델로 한 구현이 제공되며 asyncio.queues 하위 모듈을 임포트하여 액세스할 수 있습니다.

일반적으로 이러한 기능은 스레드 기반 대응 요소와 밀접하게 대응하지만, 블로킹 메서드(예: 잠금의 acquire(), 큐의 put()get())는 코루틴이며 시간 제한 매개변수는 제공되지 않습니다(단, asyncio.wait_for()를 사용하여 블로킹 호출에 시간 제한을 추가할 수 있습니다).

모듈의 독스트링에서 더 자세한 설명을 제공합니다.

잠금

다음 클래스는 asyncio.locks에서 제공합니다. Event를 제외한 모든 클래스에서는 with 블록을 어떤 방식으로 벗어나더라도 잠금이 해제되도록 보장하면서 잠금을 획득하기 위해 with 문을 yield from과 함께 다음과 같이 사용할 수 있습니다.:

with (yield from my_lock):
    ...
  • Lock: acquire()(코루틴), locked(), release() 메서드를 제공하는 기본 뮤텍스입니다.
  • Event: wait()(코루틴), set(), clear(), is_set() 메서드를 제공하는 이벤트 변수입니다.
  • Condition: acquire(), wait(), wait_for(predicate)(모두 코루틴), locked(), release(), notify(), notify_all() 메서드를 제공하는 조건 변수입니다.
  • Semaphore: acquire()(코루틴), locked(), release() 메서드를 제공하는 세마포어입니다. 생성자 인자는 초기값이며, 기본값은 1입니다.
  • BoundedSemaphore: 제한된 세마포어입니다. Semaphore와 유사하지만 초기값이 최대값이기도 합니다.

다음 클래스와 예외는 asyncio.queues에서 제공합니다.

  • Queue: get(), put()(둘 다 코루틴), get_nowait(), put_nowait(), empty(), full(), qsize(), maxsize() 메서드를 제공하는 표준 큐입니다.
  • PriorityQueue: 우선순위 순서(가장 낮은 항목부터)로 항목을 가져오는 Queue의 서브클래스입니다.
  • LifoQueue: 가장 최근에 추가된 항목부터 가져오는 Queue의 서브클래스입니다.
  • JoinableQueue: task_done()join() 메서드(후자는 코루틴)를 제공하는 Queue의 서브클래스입니다.
  • Empty, Full: 각각 비어 있거나 가득 찬 큐에서 get_nowait() 또는 put_nowait()를 호출할 때 발생하는 예외입니다.

기타

로깅

asyncio 패키지에서 수행하는 모든 로깅은 단일 logging.Logger 객체인 asyncio.logger를 사용합니다. 로깅을 사용자 지정하려면 이 객체에 표준 Logger API를 사용하십시오. (단, 객체를 교체하지는 마십시오.)

UNIX에서의 SIGCHLD 처리

서브프로세스 프로토콜에서 process_exited() 메서드를 효율적으로 구현하려면 SIGCHLD 신호 처리기가 필요합니다. 그러나 신호 처리기는 주 스레드에 연결된 이벤트 루프에서만 설정할 수 있습니다. 다른 스레드에서 실행 중인 이벤트 루프에서 서브프로세스를 생성할 수 있도록 여러 이벤트 루프 간에 SIGCHLD 처리기를 공유할 수 있는 메커니즘이 존재합니다. 두 가지 추가 함수인 asyncio.get_child_watcher()asyncio.set_child_watcher()와 이벤트 루프 정책의 해당 메서드가 있습니다.

자식 감시자 구현 클래스는 FastChildWatcherSafeChildWatcher 두 가지가 있습니다. 둘 다 SIGCHLD를 사용합니다. SafeChildWatcher 클래스가 기본적으로 사용되며, 많은 서브프로세스가 동시에 존재할 때 비효율적입니다. FastChildWatcher 클래스는 효율적이지만, asyncio 이벤트 루프를 사용하지 않고 서브프로세스를 생성하는 다른 코드(C 코드 또는 Python 코드)를 방해할 수 있습니다. 서브프로세스를 생성하는 다른 코드를 사용하고 있지 않다고 확신한다면, 빠른 구현을 사용하기 위해 주 스레드에서 다음을 실행하십시오.:

watcher = asyncio.FastChildWatcher()
asyncio.set_child_watcher(watcher)

희망 목록

(이 기능들이 바람직하다는 데에는 동의하지만, Python 3.4 베타 1이 릴리스되었을 때 구현이 제공되지 않았고, Python 3.4 릴리스 주기의 나머지 기간에 대한 기능 동결로 인해 이 늦은 단계에서 이를 추가하는 것이 금지되어 있습니다. 그러나 Python 3.5에서 추가되기를 바라며, PyPI 배포판에서는 더 일찍 추가될 수도 있습니다.)

  • TCP 소켓을 SSL/TLS로 업그레이드하는 “start TLS” 작업을 지원하십시오.

이후 구현되었지만 PEP에서 지정하지 않은 이전 희망 목록 항목은 다음과 같습니다.

  • UNIX 도메인 소켓입니다.
  • 루프별 오류 처리 콜백입니다.

미해결 문제

(이러한 사항은 PEP의 수락으로 현 상태를 따르는 방향으로 사실상 해결되었습니다. 그러나 PEP의 잠정적 상태로 인해 Python 3.5에서 이러한 결정을 수정할 수 있습니다.)

  • create_connection()create_datagram_endpoint()에는 proto 인자가 있지만 create_server()에는 없는 이유는 무엇입니까? 또한 getaddrinfo()의 family, flag, proto 인자는 어떤 경우에는 0이고 어떤 경우에는 이름이 지정된 상수(그 값도 0임)인 이유는 무엇입니까?
  • 루프가 중지되는 중인지 확인하려면 다른 조회 방법이 필요합니까?
  • Handle을 위한 더 완전한 공개 API가 필요합니까? 사용 사례는 무엇입니까?
  • 디버깅 API가 필요합니까? 예를 들어 많은 항목을 기록하거나, 비정상적인 상태(큐가 비워지는 속도보다 더 빠르게 채워지는 경우 등)를 기록하거나, 콜백에 너무 많은 시간이 걸리는 경우까지 기록하는 기능입니다…
  • 인트로스펙션 API가 필요합니까? 예를 들어 파일 디스크립터가 주어졌을 때 읽기 콜백을 요청하는 기능입니다. 또는 다음에 예약된 호출이 언제인지 확인하는 기능입니다. 또는 콜백이 등록된 파일 디스크립터 목록을 확인하는 기능입니다. 현재는 이 모든 작업에 내부 기능을 사용해야 합니다.
  • sock_sendto()sock_recvfrom()과 같은 소켓 I/O 메서드가 더 필요합니까? 그리고 pipe_read()와 같은 다른 메서드도 필요할 수 있습니다. 사용자가 직접 작성할 수 있다고 생각합니다(그리 어려운 일은 아닙니다).
  • 다양한 타임아웃을 제어하는 API가 필요할 수 있습니다. 예를 들어 DNS 확인, 연결, ssl/tls 핸드셰이크, 유휴 연결, 닫기/종료, 심지어 세션별로 소요되는 시간을 제한하고 싶을 수 있습니다. 일부 메서드에 timeout키워드 인자를 추가하는 것으로 충분할 수도 있으며, 다른 타임아웃은 call_later()Task.cancel()을 영리하게 사용하여 구현할 수 있을 것입니다. 그러나 일부 작업에는 기본 타임아웃이 필요할 수 있으며, 특정 작업의 기본값을 전역적으로(즉, 이벤트 루프별로) 변경하고 싶을 수도 있습니다.

참고 자료

감사의 말

관련 PEP 3153외에도 PEP 380, yield from에 관한 Greg Ewing의 튜토리얼, Twisted, Tornado, ZeroMQ, pyftpdlib, wattle(Steve Dower의 반대 제안)의 영향을 받았습니다. Google App Engine의 NDB 라이브러리에서 비동기 지원을 수행했던 이전 작업이 중요한 출발점이 되었습니다.

2012년 9월부터 12월까지 python-ideas에서 이루어진 수많은 논의와 그 이후 python-tulip에서 이어진 더 많은 논의, Steve Dower 및 Dino Viehland와의 Skype 세션, Ben Darnell과 주고받은 이메일 및 그의 방문, Niels Provos(libevent의 최초 작성자)와의 면담, 그리고 Glyph, Brian Warner, David Reid, Duncan McGreggor를 비롯한 여러 Twisted 개발자와의 대면 회의(잦은 이메일 교환도 포함)에 감사드립니다.

구현에 기여한 사람으로는 Eli Bendersky, Gustavo Carneiro (Gambit Research), Saúl Ibarra Corretgé, Geert Jansen, A. Jesse Jiryu Davis, Nikolay Kim, Charles-François Natali, Richard Oudkerk, Antoine Pitrou, Giampaolo Rodolá, Andrew Svetlov 및 버그 및/또는 수정 사항을 제출한 많은 분들이 포함됩니다.

공식 PEP BDFL로서 피드백을 제공해 주신 Antoine Pitrou에게 감사드립니다.