"xmlrpc.client" --- XML-RPC 클라이언트 액세스
*********************************************

**소스 코드:** Lib/xmlrpc/client.py

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

XML-RPC는 HTTP(S)를 통해 전달된 XML을 트랜스포트로 사용하는 원격 프로
시저 호출(Remote Procedure Call) 방법입니다. 이를 통해, 클라이언트는
원격 서버에서 매개 변수를 사용하여 메서드를 호출하고 (서버는 URI로 이
름이 지정됩니다) 구조화된 데이터를 돌려받을 수 있습니다. 이 모듈은
XML-RPC 클라이언트 코드 작성을 지원합니다; 적합한 파이썬 객체와 전송
회선 상의 XML 간 변환의 모든 세부 사항을 처리합니다.

경고:

  "xmlrpc.client" 모듈은 악의적으로 구성된 데이터로부터 안전하지 않습
  니다. 신뢰할 수 없거나 인증되지 않은 데이터를 구문 분석해야 하면 XML
  취약점을 참조하십시오.

버전 3.5에서 변경: HTTPS URI의 경우, "xmlrpc.client"는 이제 기본적으로
필요한 모든 인증서와 호스트명 확인을 수행합니다.

class xmlrpc.client.ServerProxy(uri, transport=None, encoding=None, verbose=False, allow_none=False, use_datetime=False, use_builtin_types=False, *, headers=(), context=None)

   "ServerProxy" 인스턴스는 원격 XML-RPC 서버와의 통신을 관리하는 객체
   입니다. 필수적인 첫 번째 인자는 URI(Uniform Resource Indicator)이며
   일반적으로 서버의 URL입니다. 선택적인 두 번째 인자는 트랜스포트 팩
   토리 인스턴스입니다; 기본적으로 https: URL의 경우는 내부
   "SafeTransport" 인스턴스이고 그렇지 않으면 내부 HTTP "Transport" 인
   스턴스입니다. 선택적 세 번째 인자는 인코딩이며, 기본적으로 UTF-8입
   니다. 선택적 네 번째 인자는 디버깅 플래그입니다.

   그 뒤에 오는 매개 변수들은 반환된 프락시 인스턴스 사용을 제어합니다
   . *allow_none*이 참이면, 파이썬 상수 "None"이 XML로 변환됩니다; 기
   본 동작은 "None"이 "TypeError"를 발생시키는 것입니다. 이것은 XML-
   RPC 명세에 일반적으로 사용되는 확장이지만, 모든 클라이언트와 서버에
   서 지원되는 것은 아닙니다; 설명은 http://ontosys.com/xml-
   rpc/extensions.php를 참조하십시오. *use_builtin_types* 플래그를 사
   용하여 날짜/시간 값을 "datetime.datetime" 객체로 표현하고 바이너리
   데이터를 "bytes" 객체로 표현할 수 있습니다; 이 플래그는 기본적으로
   거짓입니다. "datetime.datetime", "bytes" 및 "bytearray" 객체는 호출
   로 전달될 수 있습니다. *headers* 매개 변수는 각 요청과 함께 보낼 선
   택적 HTTP 헤더의 시퀀스이며, 헤더 이름과 값을 나타내는 2-튜플의 시
   퀀스로 표현됩니다. (예를 들어 *[('Header-Name', 'value')]*). 사용되
   지 않는 *use_datetime* 플래그는 *use_builtin_types*와 유사하지만,
   날짜/시간 값에만 적용됩니다.

버전 3.3에서 변경: *use_builtin_types* 플래그가 추가되었습니다.

버전 3.8에서 변경: *headers* 매개 변수가 추가되었습니다.HTTP와 HTTPS
트랜스포트는 모두 HTTP 기본 인증(Basic Authentication)을 위한 URL 구문
확장을 지원합니다: "http://user:pass@host:port/path". "user:pass" 부분
은 HTTP 'Authorization' 헤더로 base64 인코딩되고, XML-RPC 메서드를 호
출할 때 연결 프로세스의 일부로 원격 서버로 전송됩니다. 원격 서버가 기
본 인증 사용자와 비밀번호를 요구할 때만 이를 사용해야 합니다. HTTPS
URL이 제공되면, *context*는 "ssl.SSLContext" 일 수 있고 하부 HTTPS 연
결의 SSL 설정을 구성합니다.반환된 인스턴스는 원격 서버에서 해당 RPC 호
출을 호출하는 데 사용할 수 있는 메서드가 있는 프락시 객체입니다. 원격
서버가 인트로스펙션(introspection) API를 지원하면, 프락시를 사용하여
원격 서버에서 지원하는 메서드를 조회하고 (서비스 검색, service
discovery) 다른 서버 관련 메타 데이터를 가져올 수 있습니다.적합한 형(
예를 들어 XML을 통해 마샬링 할 수 있는 형)에는 다음이 포함됩니다 (별도
로 표시된 경우를 제외하고는 같은 파이썬 형으로 역마샬링 됩니다):

+------------------------+---------------------------------------------------------+
| XML-RPC 형             | 파이썬 형                                               |
|========================|=========================================================|
| "boolean"              | "bool"                                                  |
+------------------------+---------------------------------------------------------+
| "int", "i1", "i2",     | -2147483648에서 2147483647 범위의 "int". 값은 "<int>"   |
| "i4", "i8" 또는        | 태그를 얻습니다.                                        |
| "biginteger"           |                                                         |
+------------------------+---------------------------------------------------------+
| "double"이나 "float"   | "float". 값은 "<double>" 태그를 얻습니다.               |
+------------------------+---------------------------------------------------------+
| "string"               | "str"                                                   |
+------------------------+---------------------------------------------------------+
| "array"                | 적합한 요소를 포함하는 "list"나 "tuple". 배열은 "리스트 |
|                        | "로 반환됩니다.                                         |
+------------------------+---------------------------------------------------------+
| "struct"               | "dict". 키는 문자열이어야 하며, 값은 적합한 형일 수 있  |
|                        | 습니다. 사용자 정의 클래스의 객체를 전달할 수 있습니다; |
|                        | "__dict__" 어트리뷰트만 전송 됩니다.                    |
+------------------------+---------------------------------------------------------+
| "dateTime.iso8601"     | "DateTime"이나 "datetime.datetime". 반환되는 형은       |
|                        | *use_builtin_types* 와 *use_datetime* 플래그 값에 따라  |
|                        | 다릅니다.                                               |
+------------------------+---------------------------------------------------------+
| "base64"               | "Binary", "bytes" 또는 "bytearray". 반환되는 형은       |
|                        | *use_builtin_types* 플래그의 값에 따라 다릅니다.        |
+------------------------+---------------------------------------------------------+
| "nil"                  | "None" 상수. *allow_none*이 참일 때만 전달이 허용됩니다 |
|                        | .                                                       |
+------------------------+---------------------------------------------------------+
| "bigdecimal"           | "decimal.Decimal". 반환되는 형 전용.                    |
+------------------------+---------------------------------------------------------+

이것이 XML-RPC가 지원하는 데이터형의 전체 집합입니다. 메서드 호출은
XML-RPC 서버 에러를 알리는 데 사용되는 특수 "Fault" 인스턴스나
HTTP/HTTPS 전송 계층의 에러를 알리는 데 사용되는 "ProtocolError"를 발
생시킬 수도 있습니다. "Fault"와 "ProtocolError"는 모두 "Error"라는 베
이스 클래스에서 파생됩니다. xmlrpc 클라이언트 모듈은 현재 내장형의 서
브 클래스 인스턴스를 마샬링 하지 않음에 유의하십시오.문자열을 전달할
때, "<", ">" 및 "&"와 같은 XML에 특수한 문자는 자동으로 이스케이프 됩
니다. 그러나, 0에서 31 사이의 ASCII 값을 가진 제어 문자(물론 탭, 줄 넘
김 및 캐리지 리턴은 제외하고)와 같이 문자열에 XML에서 허용되지 않는 문
자가 없도록 확인하는 것은 호출자의 책임입니다; 이렇게 하지 않으면 XML
형식이 잘못된 XML-RPC 요청이 발생합니다. XML-RPC를 통해 임의의 바이트
열을 전달해야 하면, "bytes"나 "bytearray" 클래스 또는 아래 설명된
"Binary" 래퍼 클래스를 사용하십시오."Server"는 이전 버전과의 호환성을
위해 "ServerProxy"의 별칭으로 유지됩니다. 새 코드는 "ServerProxy"를 사
용해야 합니다.

버전 3.5에서 변경: *context* 인자를 추가했습니다.

버전 3.6에서 변경: 접두사가 있는 형 태그 지원이 추가되었습니다 (예를
들어 "ex:nil"). 숫자를 위해 Apache XML-RPC 구현이 사용하는 추가 형의
역마샬링 지원이 추가되었습니다: "i1", "i2", "i8", "biginteger",
"float" 및 "bigdecimal". 설명은 http://ws.apache.org/xmlrpc/types.html
을 참조하십시오.

더 보기:

  XML-RPC HOWTO
     여러 언어로 된 XML-RPC 연산과 클라이언트 소프트웨어에 대한 훌륭한
     설명. XML-RPC 클라이언트 개발자가 알아야 할 거의 모든 것이 포함되
     어 있습니다.

  XML-RPC Introspection
     인트로스펙션을 위한 XML-RPC 프로토콜 확장을 설명합니다.

  XML-RPC Specification
     공식 명세.

  Unofficial XML-RPC Errata
     Fredrik Lundh의 "비공식 정오표, XML-RPC 명세의 특정 세부 사항을
     명확하게 설명할 뿐만 아니라 여러분 자신의 XML-RPC 구현을 설계할
     때 사용할 '모범 사례'에 대한 힌트를 제공하기 위한 것입니다."


ServerProxy 객체
================

"ServerProxy" 인스턴스에는 XML-RPC 서버가 받아들이는 각 원격 프로시저
호출에 해당하는 메서드가 있습니다. 메서드를 호출하면 RPC를 수행하는데,
이름과 인자 서명 모두로 디스패치 됩니다 (예를 들어 같은 메서드 이름이
여러 인자 서명으로 오버로드 될 수 있습니다). RPC는 값을 반환하여 완료
되는데, 값은 적합한 형으로 반환된 데이터이거나 에러를 나타내는 "Fault"
나 "ProtocolError" 객체일 수 있습니다.

XML 인트로스팩션 API를 지원하는 서버는 예약된 "system" 어트리뷰트 밑에
그룹화된 몇 가지 공통 메서드를 지원합니다:

ServerProxy.system.listMethods()

   이 메서드는 문자열 리스트를 반환하는데, XML-RPC 서버가 지원하는 각
   (system이 아닌) 메서드마다 하나씩 제공됩니다.

ServerProxy.system.methodSignature(name)

   이 메서드는 하나의 매개 변수를 취하는데, XML-RPC 서버에 의해 구현된
   메서드의 이름입니다. 이 메서드에 대해 가능한 서명의 배열을 반환합니
   다. 서명은 형의 배열입니다. 이 형 중 첫 번째는 메서드의 반환형이고
   나머지는 매개 변수입니다.

   다중 서명(즉 오버로딩)이 허용되므로, 이 메서드는 하나가 아닌 서명의
   리스트를 반환합니다.

   서명 자체는 메서드가 기대하는 최상위 매개 변수로 제한됩니다. 예를
   들어, 메서드가 구조체 배열 하나를 매개 변수로 기대하고, 문자열을 반
   환하면, 서명은 단순히 "string, array"입니다. 세 개의 정수를 기대하
   고 문자열을 반환하면, 서명은 "string, int, int, int"입니다.

   메서드에 서명이 정의되지 않으면, 배열이 아닌 값이 반환됩니다. 파이
   썬에서 이것은 반환된 값의 형이 리스트 이외의 어떤 것이 됨을 의미합
   니다.

ServerProxy.system.methodHelp(name)

   이 메서드는 하나의 매개 변수를 취하는데, XML-RPC 서버에 의해 구현된
   메서드의 이름입니다. 해당 메서드의 사용법을 기술하는 설명서 문자열
   을 반환합니다. 이러한 문자열이 없으면, 빈 문자열이 반환됩니다. 설명
   서 문자열에 HTML 마크업이 포함될 수 있습니다.

버전 3.5에서 변경: "ServerProxy" 인스턴스는 하부 트랜스포트를 닫기 위
한 *컨텍스트 관리자* 프로토콜을 지원합니다.

다음은 실제 예입니다. 서버 코드:

   from xmlrpc.server import SimpleXMLRPCServer

   def is_even(n):
       return n % 2 == 0

   server = SimpleXMLRPCServer(("localhost", 8000))
   print("Listening on port 8000...")
   server.register_function(is_even, "is_even")
   server.serve_forever()

이전 서버에 대한 클라이언트 코드:

   import xmlrpc.client

   with xmlrpc.client.ServerProxy("http://localhost:8000/") as proxy:
       print("3 is even: %s" % str(proxy.is_even(3)))
       print("100 is even: %s" % str(proxy.is_even(100)))


DateTime 객체
=============

class xmlrpc.client.DateTime

   이 클래스는 에포크(epoch) 이후의 초, 시간 튜플, ISO 8601 시간/날짜
   문자열 또는 "datetime.datetime"으로 초기화될 수 있습니다. 주로 마샬
   링/역마샬링 코드 내부에서 사용하기 위해 지원되는, 다음과 같은 메서
   드가 있습니다:

   decode(string)

      인스턴스의 새 시간 값으로 문자열을 받아들입니다.

   encode(out)

      이 "DateTime" 항목의 XML-RPC 인코딩을 *out* 스트림 객체에 씁니다
      .

   풍부한 비교와 "__repr__()" 메서드를 통해 특정 파이썬 내장 연산자도
   지원합니다.

다음은 실제 예입니다. 서버 코드:

   import datetime
   from xmlrpc.server import SimpleXMLRPCServer
   import xmlrpc.client

   def today():
       today = datetime.datetime.today()
       return xmlrpc.client.DateTime(today)

   server = SimpleXMLRPCServer(("localhost", 8000))
   print("Listening on port 8000...")
   server.register_function(today, "today")
   server.serve_forever()

이전 서버에 대한 클라이언트 코드:

   import xmlrpc.client
   import datetime

   proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")

   today = proxy.today()
   # convert the ISO8601 string to a datetime object
   converted = datetime.datetime.strptime(today.value, "%Y%m%dT%H:%M:%S")
   print("Today: %s" % converted.strftime("%d.%m.%Y, %H:%M"))


Binary 객체
===========

class xmlrpc.client.Binary

   이 클래스는 바이트열 데이터(NUL을 포함할 수 있습니다)로 초기화될 수
   있습니다. "Binary" 객체의 내용에 대한 기본 액세스는 어트리뷰트에 의
   해 제공됩니다:

   data

      "Binary" 인스턴스로 캡슐화된 바이너리 데이터. 데이터는 "bytes"
      객체로 제공됩니다.

   "Binary" 객체에는 주로 마샬링/역마샬링 코드 내부에서 사용하기 위해
   지원되는 다음과 같은 메서드가 있습니다:

   decode(bytes)

      base64 "bytes" 객체를 받아들이고 인스턴스의 새 데이터로 디코딩합
      니다.

   encode(out)

      이 바이너리 항목의 XML-RPC base64 인코딩을 *out* 스트림 객체에
      씁니다.

      인코딩된 데이터에는 **RFC 2045 섹션 6.8**에 따라 76문자마다 줄
      바꿈이 있는데, 이는 XML-RPC 명세가 작성될 때 사실상 표준 base64
      명세였습니다.

   "__eq__()"와 "__ne__()" 메서드를 통해 특정 파이썬 내장 연산자도 지
   원합니다.

바이너리 객체의 사용 예. XMLRPC를 통해 이미지를 전송할 것입니다:

   from xmlrpc.server import SimpleXMLRPCServer
   import xmlrpc.client

   def python_logo():
       with open("python_logo.jpg", "rb") as handle:
           return xmlrpc.client.Binary(handle.read())

   server = SimpleXMLRPCServer(("localhost", 8000))
   print("Listening on port 8000...")
   server.register_function(python_logo, 'python_logo')

   server.serve_forever()

클라이언트는 이미지를 가져와서 파일에 저장합니다:

   import xmlrpc.client

   proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
   with open("fetched_python_logo.jpg", "wb") as handle:
       handle.write(proxy.python_logo().data)


Fault 객체
==========

class xmlrpc.client.Fault

   "Fault" 객체는 XML-RPC 결함 태그의 내용을 캡슐화합니다. Fault 객체
   에는 다음과 같은 어트리뷰트가 있습니다:

   faultCode

      결함 형을 나타내는 문자열.

   faultString

      결함과 연관된 진단 메시지가 포함된 문자열.

다음 예제에서는 복소수 형의 객체를 반환하여 의도적으로 "Fault"를 발생
시킵니다. 서버 코드:

   from xmlrpc.server import SimpleXMLRPCServer

   # A marshalling error is going to occur because we're returning a
   # complex number
   def add(x, y):
       return x+y+0j

   server = SimpleXMLRPCServer(("localhost", 8000))
   print("Listening on port 8000...")
   server.register_function(add, 'add')

   server.serve_forever()

이전 서버에 대한 클라이언트 코드:

   import xmlrpc.client

   proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
   try:
       proxy.add(2, 5)
   except xmlrpc.client.Fault as err:
       print("A fault occurred")
       print("Fault code: %d" % err.faultCode)
       print("Fault string: %s" % err.faultString)


ProtocolError 객체
==================

class xmlrpc.client.ProtocolError

   "ProtocolError" 객체는 하부 전송 계층에서의 프로토콜 에러를 기술합
   니다 (가령 URI로 명명된 서버가 없을 때 404 'not found' 에러). 다음
   과 같은 어트리뷰트가 있습니다:

   url

      에러를 일으킨 URI나 URL.

   errcode

      에러 코드.

   errmsg

      에러 메시지나 진단 문자열.

   headers

      에러를 일으킨 HTTP/HTTPS 요청의 헤더를 포함하는 딕셔너리.

다음 예에서는 잘못된 URI를 제공하여 의도적으로 "ProtocolError"를 발생
시킵니다:

   import xmlrpc.client

   # create a ServerProxy with a URI that doesn't respond to XMLRPC requests
   proxy = xmlrpc.client.ServerProxy("http://google.com/")

   try:
       proxy.some_method()
   except xmlrpc.client.ProtocolError as err:
       print("A protocol error occurred")
       print("URL: %s" % err.url)
       print("HTTP/HTTPS headers: %s" % err.headers)
       print("Error code: %d" % err.errcode)
       print("Error message: %s" % err.errmsg)


MultiCall 객체
==============

"MultiCall" 객체는 원격 서버에 대한 여러 호출을 단일 요청으로 캡슐화하
는 방법을 제공합니다 [1].

class xmlrpc.client.MultiCall(server)

   boxcar 메서드 호출에 사용되는 객체를 만듭니다. *server*는 최종 호출
   대상입니다. 결과 객체를 호출할 수 있지만, 즉시 "None"을 반환하고,
   호출 이름과 매개 변수를 "MultiCall" 객체에 저장하기만 합니다. 객체
   자체를 호출하면 저장된 모든 호출이 단일 "system.multicall" 요청으로
   전송됩니다. 이 호출의 결과는 *제너레이터*입니다; 이 제너레이터를 이
   터레이트 하면 개별 결과를 산출합니다.

이 클래스의 사용 예는 다음과 같습니다. 서버 코드:

   from xmlrpc.server import SimpleXMLRPCServer

   def add(x, y):
       return x + y

   def subtract(x, y):
       return x - y

   def multiply(x, y):
       return x * y

   def divide(x, y):
       return x // y

   # A simple server with simple arithmetic functions
   server = SimpleXMLRPCServer(("localhost", 8000))
   print("Listening on port 8000...")
   server.register_multicall_functions()
   server.register_function(add, 'add')
   server.register_function(subtract, 'subtract')
   server.register_function(multiply, 'multiply')
   server.register_function(divide, 'divide')
   server.serve_forever()

이전 서버에 대한 클라이언트 코드:

   import xmlrpc.client

   proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
   multicall = xmlrpc.client.MultiCall(proxy)
   multicall.add(7, 3)
   multicall.subtract(7, 3)
   multicall.multiply(7, 3)
   multicall.divide(7, 3)
   result = multicall()

   print("7+3=%d, 7-3=%d, 7*3=%d, 7//3=%d" % tuple(result))


편의 함수
=========

xmlrpc.client.dumps(params, methodname=None, methodresponse=None, encoding=None, allow_none=False)

   *params*를 XML-RPC 요청으로, 또는 *methodresponse*가 참이면 응답으
   로 변환합니다. *params*는 인자의 튜플이거나 "Fault" 예외 클래스의
   인스턴스일 수 있습니다. *methodresponse*가 참이면, 단일 값만 반환될
   수 있는데, *params*의 길이가 1이어야 한다는 뜻입니다. 제공되면,
   *encoding*은 생성된 XML에서 사용할 인코딩입니다; 기본값은 UTF-8입니
   다. 파이썬의 "None" 값은 표준 XML-RPC에서 사용할 수 없습니다; 확장
   을 통해 이를 사용하려면 *allow_none*에 참값을 제공하십시오.

xmlrpc.client.loads(data, use_datetime=False, use_builtin_types=False)

   XML-RPC 요청이나 응답을 파이썬 객체 "(params, methodname)"로 변환합
   니다. *params*는 인자의 튜플입니다; *methodname*은 문자열이거나 패
   킷에 메서드 이름이 없으면 "None"입니다. XML-RPC 패킷이 결함 조건을
   나타내면, 이 함수는 "Fault" 예외를 발생시킵니다.
   *use_builtin_types* 플래그를 사용하여 날짜/시간 값을
   "datetime.datetime" 객체로 표현하고 바이너리 데이터를 "bytes" 객체
   로 표현할 수 있습니다; 이 플래그는 기본적으로 거짓입니다.

   사용되지 않는 *use_datetime* 플래그는 *use_builtin_types*와 유사하
   지만, 날짜/시간 값에만 적용됩니다.

   버전 3.3에서 변경: *use_builtin_types* 플래그가 추가되었습니다.


클라이언트 사용 예
==================

   # simple test program (from the XML-RPC specification)
   from xmlrpc.client import ServerProxy, Error

   # server = ServerProxy("http://localhost:8000") # local server
   with ServerProxy("http://betty.userland.com") as proxy:

       print(proxy)

       try:
           print(proxy.examples.getStateName(41))
       except Error as v:
           print("ERROR", v)

HTTP 프락시를 통해 XML-RPC 서버에 액세스하려면, 사용자 정의 트랜스포트
를 정의해야 합니다. 다음 예제는 방법을 보여줍니다:

   import http.client
   import xmlrpc.client

   class ProxiedTransport(xmlrpc.client.Transport):

       def set_proxy(self, host, port=None, headers=None):
           self.proxy = host, port
           self.proxy_headers = headers

       def make_connection(self, host):
           connection = http.client.HTTPConnection(*self.proxy)
           connection.set_tunnel(host, headers=self.proxy_headers)
           self._connection = host, connection
           return connection

   transport = ProxiedTransport()
   transport.set_proxy('proxy-server', 8080)
   server = xmlrpc.client.ServerProxy('http://betty.userland.com', transport=transport)
   print(server.examples.getStateName(41))


클라이언트와 서버 사용 예
=========================

SimpleXMLRPCServer 예제를 참조하십시오.

-[ 각주 ]-

[1] 이 접근법은 xmlrpc.com에서의 토론에서 처음 제시되었습니다.
