# `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`, `i4`, `i8` 또는 `biginteger` | -2147483648에서 2147483647 범위의 `int`. 값은 `<int>` 태그를 얻습니다.
`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

  공식 명세.

## 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에서의 토론에서 처음 제시되었습니다.