"nntplib" --- NNTP 프로토콜 클라이언트
**************************************

**소스 코드:** Lib/nntplib.py

버전 3.11부터 폐지: The "nntplib" module is deprecated (see **PEP
594** for details).

======================================================================

이 모듈은 네트워크 뉴스 전송 프로토콜(Network News Transfer Protocol)
의 클라이언트 측을 구현하는 클래스 "NNTP"를 정의합니다. 뉴스 리더나 포
스터 또는 자동화된 뉴스 프로세서를 구현하는 데 사용할 수 있습니다. 이
전 **RFC 977**과 **RFC 2980**뿐만 아니라 **RFC 3977**과 호환됩니다.

다음은 사용 방법에 대한 두 가지 작은 예입니다. 뉴스 그룹에 대한 일부
통계를 나열하고 최근 10개 기사의 제목(subject)을 인쇄하려면 이렇게 합
니다:

   >>> s = nntplib.NNTP('news.gmane.io')
   >>> resp, count, first, last, name = s.group('gmane.comp.python.committers')
   >>> print('Group', name, 'has', count, 'articles, range', first, 'to', last)
   Group gmane.comp.python.committers has 1096 articles, range 1 to 1096
   >>> resp, overviews = s.over((last - 9, last))
   >>> for id, over in overviews:
   ...     print(id, nntplib.decode_header(over['subject']))
   ...
   1087 Re: Commit privileges for Łukasz Langa
   1088 Re: 3.2 alpha 2 freeze
   1089 Re: 3.2 alpha 2 freeze
   1090 Re: Commit privileges for Łukasz Langa
   1091 Re: Commit privileges for Łukasz Langa
   1092 Updated ssh key
   1093 Re: Updated ssh key
   1094 Re: Updated ssh key
   1095 Hello fellow committers!
   1096 Re: Hello fellow committers!
   >>> s.quit()
   '205 Bye!'

바이너리 파일에서 기사를 게시하려면 (기사에 유효한 헤더가 있고 특정 뉴
스 그룹에 게시할 권한이 있다고 가정합니다):

   >>> s = nntplib.NNTP('news.gmane.io')
   >>> f = open('article.txt', 'rb')
   >>> s.post(f)
   '240 Article posted successfully.'
   >>> s.quit()
   '205 Bye!'

모듈 자체는 다음 클래스를 정의합니다:

class nntplib.NNTP(host, port=119, user=None, password=None, readermode=None, usenetrc=False[, timeout])

   *port* 포트에서 리스닝하면서 호스트 *host*에서 실행 중인 NNTP 서버
   에 대한 연결을 나타내는 새 "NNTP" 객체를 반환합니다. 소켓 연결에 대
   한 선택적 *timeout*을 지정할 수 있습니다. 선택적 *user*와
   *password*가 제공되거나, "/.netrc"에 적합한 자격 증명이 존재하고 선
   택적 플래그 *usenetrc*가 참이면, "AUTHINFO USER"와 "AUTHINFO PASS"
   명령이 서버에 사용자를 식별하고 인증하는 데 사용됩니다. 선택적 플래
   그 *readermode*가 참이면, 인증이 수행되기 전에 "mode reader" 명령이
   전송됩니다. 로컬 시스템의 NNTP 서버에 연결하고 "group"과 같은 리더
   특정 명령을 호출하려면 때때로 리더 모드가 필요합니다. 예기치 않은
   "NNTPPermanentError" 가 발생하면, *readermode*를 설정해야 합니다.
   "NNTP" 클래스는 "OSError" 예외를 조건 없이 소비하고 완료 시 NNTP 연
   결을 닫는 "with" 문을 지원합니다. 예를 들면:

   >>> from nntplib import NNTP
   >>> with NNTP('news.gmane.io') as n:
   ...     n.group('gmane.comp.python.committers')
   ... 
   ('211 1755 1 1755 gmane.comp.python.committers', 1755, 1, 1755, 'gmane.comp.python.committers')
   >>>

   인자 "self", "host", "port"로 감사 이벤트 "nntplib.connect"를 발생
   시킵니다.

   인자 "self", "line"으로 감사 이벤트 "nntplib.putline"을 발생시킵니
   다.

   버전 3.2에서 변경: *usenetrc*는 이제 기본적으로 "False"입니다.

   버전 3.3에서 변경: "with" 문에 대한 지원이 추가되었습니다.

   버전 3.9에서 변경: *timeout* 매개 변수가 0으로 설정되면, 비 블로킹
   소켓이 만들어지지 않도록 "ValueError"가 발생합니다.

class nntplib.NNTP_SSL(host, port=563, user=None, password=None, ssl_context=None, readermode=None, usenetrc=False[, timeout])

   *port* 포트에서 리스닝하면서 *host* 호스트에서 실행 중인 NNTP 서버
   에 대한 암호화 된 연결을 나타내는 새 "NNTP_SSL" 객체를 반환합니다.
   "NNTP_SSL" 객체는 "NNTP" 객체와 같은 메서드를 갖습니다. *port*를 생
   략하면, 포트 563(NNTPS)이 사용됩니다. *ssl_context*도 선택적이며,
   "SSLContext" 객체입니다. 모범 사례를 보려면 보안 고려 사항을 읽으십
   시오. 다른 모든 매개 변수는 "NNTP"와 같게 작동합니다.

   SSL-on-563은 **RFC 4642**에 따라 권장되지 않고, 아래 설명된
   STARTTLS로 대체합니다. 그러나, 일부 서버는 전자만 지원합니다.

   인자 "self", "host", "port"로 감사 이벤트 "nntplib.connect"를 발생
   시킵니다.

   인자 "self", "line"으로 감사 이벤트 "nntplib.putline"을 발생시킵니
   다.

   버전 3.2에 추가.

   버전 3.4에서 변경: 이 클래스는 이제 "ssl.SSLContext.check_hostname"
   과 *서버 이름 표시(Server Name Indication)*("ssl.HAS_SNI"를 참조하
   십시오)로 호스트명 확인을 지원합니다.

   버전 3.9에서 변경: *timeout* 매개 변수가 0으로 설정되면, 비 블로킹
   소켓이 만들어지지 않도록 "ValueError"가 발생합니다.

exception nntplib.NNTPError

   표준 예외 "Exception"에서 파생된 이 클래스는 "nntplib" 모듈이 발생
   시키는 모든 예외의 베이스 클래스입니다. 이 클래스의 인스턴스는 다음
   과 같은 어트리뷰트를 갖습니다:

   response

      사용할 수 있으면 서버의 응답, "str" 객체.

exception nntplib.NNTPReplyError

   서버에서 예기치 않은 응답이 수신될 때 발생하는 예외.

exception nntplib.NNTPTemporaryError

   400--499 범위의 응답 코드가 수신될 때 발생하는 예외.

exception nntplib.NNTPPermanentError

   500--599 범위의 응답 코드가 수신될 때 발생하는 예외.

exception nntplib.NNTPProtocolError

   서버에서 1--5 범위의 숫자로 시작하지 않는 응답을 수신할 때 발생하는
   예외.

exception nntplib.NNTPDataError

   응답 데이터에 에러가 있을 때 발생하는 예외.


NNTP 객체
=========

연결되었을 때, "NNTP"와 "NNTP_SSL" 객체는 다음과 같은 메서드와 어트리
뷰트를 지원합니다.


어트리뷰트
----------

NNTP.nntp_version

   서버에서 지원하는 NNTP 프로토콜 버전을 나타내는 정수. 실제로, **RFC
   3977** 준수를 광고하는 서버의 경우 "2"이고 다른 서버의 경우 "1"이어
   야 합니다.

   버전 3.2에 추가.

NNTP.nntp_implementation

   NNTP 서버의 소프트웨어 이름과 버전을 기술하는 문자열, 또는 서버가
   알리지 않으면 "None".

   버전 3.2에 추가.


메서드
------

거의 모든 메서드의 반환 튜플에서 첫 번째 항목으로 반환되는 *response*
는 서버의 응답입니다: 3 자리 숫자 코드로 시작하는 문자열. 서버의 응답
이 에러를 가리키면, 메서드는 위의 예외 중 하나를 발생시킵니다.

다음 메서드 중 다수는 선택적 키워드 전용 인자 *file*을 취합니다.
*file* 인자가 제공될 때, 바이너리 쓰기를 위해 열린 *파일 객체*이거나,
기록될 디스크에 있는 파일의 이름이어야 합니다. 그러면 메서드는 서버가
반환한 모든 데이터를 (응답 줄과 종료 점을 제외하고) 파일에 기록합니다;
메서드가 일반적으로 반환하는 줄, 튜플 또는 객체의 리스트는 비어 있습니
다.

버전 3.2에서 변경: 다음 메서드 중 많은 부분이 재작업 및 수정되어, 3.1
과 호환되지 않습니다.

NNTP.quit()

   "QUIT" 명령을 전송하고 연결을 닫습니다. 일단, 이 메서드가 호출되면,
   NNTP 객체의 다른 메서드를 호출하면 안 됩니다.

NNTP.getwelcome()

   초기 연결에 대한 응답으로 서버에서 보낸 환영 메시지를 반환합니다. (
   이 메시지에는 때때로 사용자와 관련될 수 있는 고지 사항이나 도움말
   정보가 포함되어 있습니다.)

NNTP.getcapabilities()

   서버가 광고한 **RFC 3977** 기능을 기능 이름을 값 리스트(비어있을 수
   있습니다)로 매핑하는 "dict" 인스턴스로 반환합니다. "CAPABILITIES"
   명령을 이해하지 못하는 레거시 서버에서는, 빈 딕셔너리가 대신 반환됩
   니다.

   >>> s = NNTP('news.gmane.io')
   >>> 'POST' in s.getcapabilities()
   True

   버전 3.2에 추가.

NNTP.login(user=None, password=None, usenetrc=True)

   사용자 이름과 비밀번호로 "AUTHINFO" 명령을 보냅니다. *user*와
   *password*가 "None"이고 *usenetrc*가 참이면 가능한 경우 "~/.netrc"
   의 자격 증명이 사용됩니다.

   의도적으로 지연되지 않는 한, 일반적으로 "NNTP" 객체 초기화 중에 로
   그인이 수행되며 별도로 이 함수를 호출할 필요가 없습니다. 인증을 강
   제로 지연시키려면, 객체를 만들 때 *user*나 *password*를 설정하지 말
   고, *usenetrc*를 False로 설정해야 합니다.

   버전 3.2에 추가.

NNTP.starttls(context=None)

   "STARTTLS" 명령을 보냅니다. 이것은 NNTP 연결에서 암호화를 활성화합
   니다. *context* 인자는 선택적이며 "ssl.SSLContext" 객체여야 합니다.
   모범 사례를 보려면 보안 고려 사항을 읽으십시오.

   인증 정보가 전송된 후에는 이 작업이 수행되지 않을 수 있으며, "NNTP"
   객체 초기화 중에 가능한 경우 기본적으로 인증이 수행됩니다. 이 동작
   을 억제하는 것에 대한 정보는 "NNTP.login()"을 참조하십시오.

   버전 3.2에 추가.

   버전 3.4에서 변경: 이 메서드는 이제 "ssl.SSLContext.check_hostname"
   과 *서버 이름 표시(Server Name Indication)*("ssl.HAS_SNI"를 참조하
   십시오)로 호스트명 확인을 지원합니다.

NNTP.newgroups(date, *, file=None)

   "NEWGROUPS" 명령을 보냅니다. *date* 인자는 "datetime.date"나
   "datetime.datetime" 객체여야 합니다. "(response, groups)" 쌍을 반환
   합니다. 여기서 *groups*는 지정된 *date* 이후의 새로운 그룹을 나타내
   는 리스트입니다. 그러나 *file*이 제공되면, *groups*는 비어 있습니다
   .

   >>> from datetime import date, timedelta
   >>> resp, groups = s.newgroups(date.today() - timedelta(days=3))
   >>> len(groups) 
   85
   >>> groups[0] 
   GroupInfo(group='gmane.network.tor.devel', last='4', first='1', flag='m')

NNTP.newnews(group, date, *, file=None)

   "NEWNEWS" 명령을 보냅니다. 여기서 *group*은 그룹 이름이나 "'*'"이며
   , *date*는 "newgroups()"에서와 같은 의미입니다. "(response,
   articles)" 쌍을 반환합니다. 여기서 *articles*는 메시지 id의 리스트
   입니다.

   이 명령은 NNTP 서버 관리자가 자주 비활성화합니다.

NNTP.list(group_pattern=None, *, file=None)

   "LIST"나 "LIST ACTIVE" 명령을 전송합니다. 쌍 "(response, list)"를
   반환합니다. 여기서 *list*는 이 NNTP 서버에서 사용 가능한 모든 그룹
   을 나타내는 튜플 리스트이며, 선택적으로 패턴 문자열 *group_pattern*
   과 일치합니다. 각 튜플의 형식은 "(group, last, first, flag)"입니다.
   여기서 *group*은 그룹 이름이고 *last*와 *first*는 마지막과 첫 번째
   기사 번호이며, *flag*는 일반적으로 다음 값 중 하나를 취합니다:

   * "y": 로컬 게시물과 동료의 기사가 허용됩니다.

   * "m": 그룹이 조정되며 모든 게시가 승인되어야 합니다.

   * "n": 로컬 게시물이 허용되지 않으며, 동료의 기사만 허용됩니다.

   * "j": 동료의 기사가 대신 정크 그룹에 보관됩니다.

   * "x": 로컬 게시물이 없으며, 동료의 기사는 무시됩니다.

   * "=foo.bar": 기사가 "foo.bar" 그룹에 대신 보관됩니다.

   *flag*에 다른 값이 있으면, 뉴스 그룹의 상태를 알 수 없는 것으로 간
   주해야 합니다.

   이 명령은 특히 *group_pattern*이 지정되지 않으면 매우 큰 결과를 반
   환할 수 있습니다. 실제로 새로 고칠 필요가 없으면 결과를 오프라인으
   로 캐시 하는 것이 가장 좋습니다.

   버전 3.2에서 변경: *group_pattern*이 추가되었습니다.

NNTP.descriptions(grouppattern)

   *grouppattern*이 **RFC 3977**에 지정된 와일드 매트(wildmat) 문자열
   (DOS나 UNIX 셸 와일드카드 문자열과 본질적으로 동일합니다)인, "LIST
   NEWSGROUPS" 명령을 전송합니다. "(response, descriptions)" 쌍을 반환
   합니다. 여기서 *descriptions*는 그룹 이름을 텍스트 설명에 매핑하는
   딕셔너리입니다.

   >>> resp, descs = s.descriptions('gmane.comp.python.*')
   >>> len(descs) 
   295
   >>> descs.popitem() 
   ('gmane.comp.python.bio.general', 'BioPython discussion list (Moderated)')

NNTP.description(group)

   단일 그룹 *group*에 대한 설명을 가져옵니다. 둘 이상의 그룹이 일치하
   면 ('group'이 실제 와일드 매트 문자열이면), 첫 번째 일치를 반환합니
   다. 일치하는 그룹이 없으면, 빈 문자열을 반환합니다.

   이것은 서버에서 온 응답 코드를 제거합니다. 응답 코드가 필요하면,
   "descriptions()"를 사용하십시오.

NNTP.group(name)

   "GROUP" 명령을 전송합니다. 여기서 *name*은 그룹 이름입니다. 존재하
   면, 그룹이 현재 그룹으로 선택됩니다. 튜플 "(response, count, first,
   last, name)"을 반환합니다. 여기서 *count*는 그룹의 (추정된) 기사 수
   , *first*는 그룹의 첫 번째 기사 번호, *last*는 그룹의 마지막 기사
   번호, *name*은 그룹 이름입니다.

NNTP.over(message_spec, *, file=None)

   "OVER" 명령이나 레거시 서버에서는 "XOVER" 명령을 보냅니다.
   *message_spec*은 메시지 id를 나타내는 문자열이거나, 현재 그룹의 기
   사 범위를 나타내는 "(first, last)" 튜플, 또는 현재 그룹의 *first*에
   서 마지막 기사까지의 기사 범위를 나타내는 "(first, None)" 튜플이거
   나, 또는 현재 그룹에서 현재 기사를 선택하는 "None"입니다.

   쌍 "(response, overviews)"를 반환합니다. *overviews*는
   *message_spec*으로 선택한 기사마다 하나씩 "(article_number,
   overview)" 튜플의 리스트입니다. 각 *overview*는 같은 수의 항목을 가
   진 딕셔너리이지만, 이 숫자는 서버에 따라 다릅니다. 이러한 항목은 메
   시지 헤더(키는 소문자 헤더 이름이 됩니다)나 메타 데이터 항목(키는
   "":""이 앞에 붙은 메타 데이터 이름이 됩니다)입니다. 다음 항목은
   NNTP 명세에 따라 제공됨이 보장됩니다:

   * "subject", "from", "date", "message-id" 및 "references" 헤더

   * ":bytes" 메타 데이터: 전체 원본 아티클의 바이트 수 (헤더와 본문을
     포함합니다)

   * ":lines" 메타 데이터: 기사 본문의 줄 수

   각 항목의 값은 문자열이거나, 존재하지 않으면 "None"입니다.

   비 ASCII 문자를 포함할 수 있는 헤더 값에 "decode_header()" 함수를
   사용하는 것이 좋습니다:

      >>> _, _, first, last, _ = s.group('gmane.comp.python.devel')
      >>> resp, overviews = s.over((last, last))
      >>> art_num, over = overviews[0]
      >>> art_num
      117216
      >>> list(over.keys())
      ['xref', 'from', ':lines', ':bytes', 'references', 'date', 'message-id', 'subject']
      >>> over['from']
      '=?UTF-8?B?Ik1hcnRpbiB2LiBMw7Z3aXMi?= <martin@v.loewis.de>'
      >>> nntplib.decode_header(over['from'])
      '"Martin v. Löwis" <martin@v.loewis.de>'

   버전 3.2에 추가.

NNTP.help(*, file=None)

   "HELP" 명령을 보냅니다. *list*가 도움말 문자열의 리스트인
   "(response, list)" 쌍을 반환합니다.

NNTP.stat(message_spec=None)

   "STAT" 명령을 보냅니다. 여기서 *message_spec*은 메시지 id("'<'"과
   "'>'"로 감싼)이거나 현재 그룹의 기사 번호입니다. *message_spec*이
   생략되거나 "None"이면, 현재 그룹의 현재 기사가 고려됩니다. *number*
   가 기사 번호이고 *id*는 메시지 id인 트리플 "(response, number, id)"
   를 반환합니다.

   >>> _, _, first, last, _ = s.group('gmane.comp.python.devel')
   >>> resp, number, message_id = s.stat(first)
   >>> number, message_id
   (9099, '<20030112190404.GE29873@epoch.metaslash.com>')

NNTP.next()

   "NEXT" 명령을 보냅니다. "stat()"과 같은 것을 반환합니다.

NNTP.last()

   "LAST" 명령을 보냅니다. "stat()"과 같은 것을 반환합니다.

NNTP.article(message_spec=None, *, file=None)

   "ARTICLE" 명령을 보냅니다. 여기서 *message_spec*은 "stat()"에서와
   같은 의미입니다. 튜플 "(response, info)"를 반환합니다. 여기서
   *info*는 3개의 어트리뷰트 *number*, *message_id* 및 *lines*(순서대
   로)가 있는 "namedtuple"입니다. *number*는 그룹의 기사 번호 (또는 정
   보가 없으면 0), *message_id*는 문자열 메시지 id, *lines*는 헤더와
   본문을 포함하는 원시 메시지를 구성하는 (종료 줄 바꿈 없는) 줄의 리
   스트입니다.

   >>> resp, info = s.article('<20030112190404.GE29873@epoch.metaslash.com>')
   >>> info.number
   0
   >>> info.message_id
   '<20030112190404.GE29873@epoch.metaslash.com>'
   >>> len(info.lines)
   65
   >>> info.lines[0]
   b'Path: main.gmane.org!not-for-mail'
   >>> info.lines[1]
   b'From: Neal Norwitz <neal@metaslash.com>'
   >>> info.lines[-3:]
   [b'There is a patch for 2.3 as well as 2.2.', b'', b'Neal']

NNTP.head(message_spec=None, *, file=None)

   "article()"과 같지만, "HEAD" 명령을 보냅니다. 반환된 (또는 *file*에
   기록되는) *lines*는 본문이 아닌 메시지 헤더만 포함합니다.

NNTP.body(message_spec=None, *, file=None)

   "article()"과 같지만, "BODY" 명령을 보냅니다. 반환된 (또는 *file*에
   기록되는) *lines*는 헤더가 아닌 메시지 본문만 포함합니다.

NNTP.post(data)

   "POST" 명령을 사용하여 기사를 게시합니다. *data* 인자는 바이너리 읽
   기를 위해 열린 *파일 객체*, 또는 바이트열 객체의 이터러블(게시할 기
   사의 원시 줄을 나타냅니다)입니다. 필수 헤더를 포함하여 올바르게 구
   성된 뉴스 기사를 나타내야 합니다. "post()" 메서드는 "."으로 시작하
   는 줄을 자동으로 이스케이프하고 종료 줄을 추가합니다.

   메서드가 성공하면, 서버의 응답이 반환됩니다. 서버가 게시를 거부하면
   , "NNTPReplyError" 가 발생합니다.

NNTP.ihave(message_id, data)

   "IHAVE" 명령을 보냅니다. *message_id*는 서버로 보낼 메시지의 id입니
   다 ("'<'"과 "'>'"로 감쌉니다). *data* 매개 변수와 반환 값은
   "post()"와 같습니다.

NNTP.date()

   쌍 "(response, date)"를 반환합니다. *date*는 서버의 현재 날짜와 시
   간을 포함하는 "datetime" 객체입니다.

NNTP.slave()

   "SLAVE" 명령을 보냅니다. 서버의 *응답*을 반환합니다.

NNTP.set_debuglevel(level)

   인스턴스의 디버깅 수준을 설정합니다. 인쇄되는 디버깅 출력량을 제어
   합니다. 기본 "0"은 디버깅 출력을 생성하지 않습니다. "1" 값은 요청이
   나 응답마다 한 줄씩 적당한 양의 디버깅 출력을 생성합니다. "2" 이상
   의 값은 최대량의 디버깅 출력을 생성하여, 연결에서 주고받은 각 줄(메
   시지 텍스트를 포함합니다)을 로깅 합니다.

다음은 **RFC 2980**에 정의된 선택적 NNTP 확장입니다. 이 중 일부는
**RFC 3977**에서 새로운 명령으로 대체되었습니다.

NNTP.xhdr(hdr, str, *, file=None)

   "XHDR" 명령을 보냅니다. *hdr* 인자는 헤더 키워드입니다, 예를 들어
   "'subject'". *str* 인자는 "'first-last'" 형식이어야 합니다. 여기서
   *first*와 *last*는 검색할 첫 번째와 마지막 기사 번호입니다. 쌍
   "(response, list)"를 반환합니다. 여기서 *list*는 쌍 "(id, text)"의
   리스이고, *id*는 기사 번호(문자열)이고, *text*는 해당 기사에 대해
   요청된 헤더의 텍스트입니다. *file* 매개 변수가 제공되면, "XHDR" 명
   령의 출력이 파일에 저장됩니다. *file*이 문자열이면, 메서드는 해당
   이름의 파일을 열고, 쓰고 나서 닫습니다. *file*이 *파일 객체*이면
   "write()"를 호출하여 명령 출력의 줄을 저장합니다. *file*이 제공되면
   , 반환된 *list*는 빈 리스트입니다.

NNTP.xover(start, end, *, file=None)

   "XOVER" 명령을 보냅니다. *start*와 *end*는 선택할 기사 범위를 정하
   는 기사 번호입니다. 반환 값은 "over()"와 같습니다. 사용할 수 있으면
   최신 "OVER" 명령을 자동으로 사용하므로 "over()"를 대신 사용하는 것
   이 좋습니다.


유틸리티 함수
=============

이 모듈은 다음과 같은 유틸리티 함수도 정의합니다:

nntplib.decode_header(header_str)

   모든 이스케이프 된 비 ASCII 문자를 역 이스케이프 하여, 헤더 값을 디
   코딩합니다. *header_str*은 "str" 객체여야 합니다. 이스케이프 처리되
   지 않은 값이 반환됩니다. 사람이 읽을 수 있는 형식으로 일부 헤더를
   표시하려면 이 함수를 사용하는 것이 좋습니다:

      >>> decode_header("Some subject")
      'Some subject'
      >>> decode_header("=?ISO-8859-15?Q?D=E9buter_en_Python?=")
      'Débuter en Python'
      >>> decode_header("Re: =?UTF-8?B?cHJvYmzDqG1lIGRlIG1hdHJpY2U=?=")
      'Re: problème de matrice'
