투명 프록시(지원 중단됨)

Warning

이 기능은 오래전에 실험적으로 시도했던 것이며, 정말 좋은 사용 사례는 찾지 못했습니다. 기본 기능은 아직 남아 있지만, 사용을 권장하지는 않습니다. 아래 예제 중 일부는 더 이상 동작하지 않을 수 있습니다(예를 들어, 이제는 리스트 객체를 tproxy할 수 없습니다). 나머지는 표준 Python에서 해킹하는 방식으로 구현할 수 있습니다. tproxy 작업을 다시 해 보고 싶은 분이 있다면 환영하지만, 저희는 이것을 흥미로운 확장으로 여기지 않습니다.

PyPy의 투명 프록시(Transparent Proxies)를 사용하면 객체에 대한 연산을 호출 가능한 객체(callable)로 라우팅할 수 있습니다. 애플리케이션 수준 코드는 타입 시스템에 간섭하지 않고 객체를 사용자 정의할 수 있으며(proxied_list가 프록시된 내장 list일 때 type(proxied_list) is list는 참입니다), 동시에 proxied_list에 대해 수행되는 모든 연산을 완전히 제어할 수 있습니다.

투명 프록시(transparent proxy)의 자세한 배경, 동기, 사용법은 [D12.1]을 참조하십시오.

핵심 메커니즘의 예

다음 예제는 리스트를 프록시하며, 모든 덧셈 연산에 대해 42를 반환합니다:

$ py.py --objspace-std-withtproxy
>>>> from __pypy__ import tproxy
>>>> def f(operation, *args, **kwargs):
>>>>    if operation == '__add__':
>>>>         return 42
>>>>    raise AttributeError
>>>>
>>>> i = tproxy(list, f)
>>>> type(i)
list
>>>> i + 3
42

내장 객체(builtins)에 대한 모든 연산을 기록하는 예

나중에 분석할 수 있도록, 자신에게 수행된 모든 연산을 저장하는 리스트가 필요하다고 가정해 봅시다. 내장 인스턴스를 투명하게 프록시하는 작업에는 lib_pypy/tputil.py 모듈의 도움을 받을 수 있습니다:

from tputil import make_proxy

history = []
def recorder(operation):
    history.append(operation)
    return operation.delegate()

>>>> l = make_proxy(recorder, obj=[])
>>>> type(l)
list
>>>> l.append(3)
>>>> len(l)
1
>>>> len(history)
2

make_proxy(recorder, obj=[])는 연산을 recorder() 함수에 위임할 수 있게 해 주는 투명한 리스트 프록시를 생성합니다. type(l)을 호출해도 연산이 전혀 실행되지 않습니다.

append()가 __getattribute__()로 나타나고 type(l)은 전혀 나타나지 않는다는 점에 유의하십시오 - 타입은 인스턴스에서 프록시 컨트롤러가 변경할 수 없는 유일한 측면입니다.

투명 프록시(Transparent Proxy)를 위한 PyPy 내장 기능 및 지원

–objspace-std-withtproxy 옵션을 사용하는 경우 __pypy__ 모듈은 다음 내장 함수를 제공합니다:

tproxy(type, controller)

주어진 타입을 나타내며 이 타입에 대한 모든 연산을 컨트롤러로 전달하는 프록시 객체를 반환합니다. 각 연산마다 controller(opname, *args, **kwargs)가 호출됩니다.

get_tproxy_controller(obj)

주어진 객체를 담당하는 컨트롤러를 반환합니다. 프록시되지 않은 객체에 대해서는 None이 반환됩니다.

tputil 헬퍼 모듈

lib_pypy/tputil.py 모듈은 다음을 제공합니다:

make_proxy(controller, type, obj)

주어진 controller 호출 가능 객체가 제어하는 투명 프록시를 생성합니다. 프록시는 주어진 타입의 완전히 평범한 인스턴스처럼 보이지만, 프록시에 대한 모든 연산은 지정된 컨트롤러로 전달됩니다. 컨트롤러는 연산이 일어날 때마다 ProxyOperation 인스턴스를 받습니다. type을 지정하지 않은 경우, obj가 지정되어 있으면 기본값은 type(obj)입니다.

ProxyOperation 인스턴스에는 다음과 같은 속성이 있습니다:

proxyobj

이 연산의 투명 프록시 객체입니다.

opname

이 연산의 이름입니다.

args

이 연산에 전달하는 위치 인자입니다.

kwargs

이 연산에 사용할 키워드 인자입니다.

obj

(make_proxy()에 제공된 경우에만)

구체 객체입니다.

delegate()

make_proxy() 호출 시 구체적인 객체 인스턴스 obj가 지정되었다면, proxyoperation.delegate()를 호출하여 해당 연산을 그 객체 인스턴스에 위임할 수 있습니다.

그 밖의 주요 사항

투명 프록시를 사용하면 많은 작업을 수행할 수 있으며, 다음과 같은 작업이 포함되지만 이에 국한되지는 않습니다.

  • 직접 연산을 수행할 수 있는 원격 버전의 객체(투명한 분산을 생각해 보십시오)
  • 데이터베이스와 같은 영구 저장소에 대한 접근(다른 객체와 똑같이 보이는 SQL 객체 매퍼를 떠올려 보십시오).
  • 다른 언어의 자료 구조와 같은 외부 자료 구조에 일반 객체처럼 접근하는 기능(물론 일부 연산은 예외를 발생시킬 수 있지만, 연산이 애플리케이션 수준에서 실행되므로 큰 문제는 아닙니다)

구현 참고 사항

PyPy의 표준 객체 공간(object space)을 사용하면 내부적으로 하나의 타입에 여러 구현을 두고 런타임에 구현을 바꿀 수 있으며, 그동안에도 애플리케이션 수준 코드는 항상 정확히 같은 타입과 객체를 보게 됩니다. 이러한 기능을 활용한 여러 성능 최적화가 이미 구현되어 있습니다: 대체 객체 구현. 투명 프록시(Transparent Proxy)는 이 아키텍처를 사용하여 제어권을 다시 애플리케이션 수준 코드에 넘겨줍니다.

투명 프록시는 표준 객체 공간(object space)을 기반으로 pypy/objspace/std/proxyobject.py, pypy/objspace/std/proxyobject.py, pypy/objspace/std/transparent.py에 구현되어 있습니다. 투명 프록시를 사용하려면 pypy 또는 translate.py에 –objspace-std-withtproxy 옵션을 전달해야 합니다. 이 옵션은 W_TransparentXxx라는 이름의 구현을 등록하며(이 구현은 보통 적절한 W_XxxObject에 대응합니다), 인터프리터와 너무 밀접하여 표준 객체 공간(object space)에서 구현할 수 없는 객체를 위한 몇 가지 인터프리터 핵(hack)도 포함합니다. 이 방식으로 프록시할 수 있는 객체 유형은 사용자가 만든 클래스와 함수, 리스트, 딕셔너리, 예외, 트레이스백, 프레임입니다.

[D12.1]High-Level Backends and Interpreter Feature Prototypes, PyPy EU-Report, 2007, https://foss.heptapod.net/pypy/extradoc/-/tree/branch/extradoc/eu-report/D12.1_H-L-Backends_and_Feature_Prototypes-2007-03-22.pdf