최신 릴리스로 업그레이드 하기

클릭은 최대한 높은 수준의 하위 호환성을 추구하지만 그게 완전히 가능하지는 않을 때가 있다. 하위 호환성이 깨지는 경우에 이 문서에서 업그레이드 방법 내지 올바른 하위 호환성 처리 방법을 설명한다.

3.2 업그레이드

클릭 3.2에서는 다중 명령에 두 가지 변화를 줘야 했다. 클릭 2와 클릭 3 사이의 어느 변경 사항에서 유발된 건데 영향이 예상보다 커졌다.

문맥 호출

클릭 3.2에는 Context.invoke() 함수를 다른 명령과 함께 쓸 때에 대한 수정 사항이 포함돼 있다. 이 함수의 원래 목적은 함수가 아니라 문맥 객체를 받았을 때 다른 명령을 명령행에서 들어가는 것처럼 호출하는 것이었다. 이런 용도가 이전에는 문서 상의 한 곳에만 기록돼 있었고 API 문서에 이 메소드에 대한 제대로 된 설명이 없었다.

핵심 문제는 3.2 전에 이 호출이 의도와 어긋나게 동작했다는 것이다.

ctx.invoke(other_command, 'arg1', 'arg2')

절대 이걸 의도한 게 아니다. 이렇게 하면 클릭에서 매개변수를 어떻게 건드릴 수가 없게 된다. 이 사용 패턴은 한 번도 문서화 된 적이 없고 의도에 어긋나는 것이다. 그래서 자칫 사용이 확산돼서 여러 개발자들이 쓰게 되기 전에 버그 수정 릴리스에서 동작 방식을 바꾸기로 결정이 이뤄졌다.

위 명령을 올바로 호출하는 방식은 다음과 같다.

ctx.invoke(other_command, name_of_arg1='arg1', name_of_arg2='arg2')

이렇게 하면 이 함수가 기본값을 제대로 다루지 못했던 문제까지 고칠 수 있게 된다.

다중 명령 연쇄 API

클릭 3에서 다중 명령 연쇄 사용 기능이 추가됐다. 이를 위해선 클릭 내부에서 보내기를 하는 방식을 바꿔야 했다. 그런데 그게 올바로 구현되질 않았으며 상위 명령에게 호출될 모든 하위 명령에 대해 알려 줄 수 있는 API를 제공하는 게 가능해 보였다.

하지만 그 가정은 과거에 API에서 제시한 보장 사항 한 가지와 충돌한다. 그래서 안그래도 이미 동작에 문제가 있던 그 기능성을 3.2에서 제거했다. 대신 의도치 않게 문제가 생겼던 Context.invoked_subcommand 속성의 기능성을 되살렸다.

정확히 어떤 명령들이 호출될지 알아야 할 필요가 있다면 여러 대처 방법이 있다. 첫째로 가능한 방법은 하위 명령 모두가 함수를 반환하도록 하고서 Context.resultcallback()에서 그 함수들을 호출하는 것이다.

2.0 업그레이드

클릭 2.0에는 호환성을 깨는 변화가 한 가지 있는데 바로 매개변수 콜백 시그너처다. 2.0 전에선 (ctx, value)로 콜백을 호출했지만 이제는 (ctx, param, value)다. 이렇게 안 하면 콜백 재사용이 너무 복잡해지기 때문에 바꿀 필요가 있었다.

이전을 돕기 위해 클릭에서는 계속 구식 콜백을 받게 된다. 클릭 3.0부터는 stderr로 경로를 찍어서 업그레이드를 권장할 것이다.

클릭 1.0과 클릭 2.0을 모두 지원하고 싶은 경우에는 시그너처를 조정해 주는 간단한 데코레이터를 만들면 된다.

import click
from functools import update_wrapper

def compatcallback(f):
    # 클릭 1.0에는 버전 문자열이 없으므로 안전을 위해
    # getattr을 써야 한다.
    if getattr(click, '__version__', '0.0') >= '2.0':
        return f
    return update_wrapper(lambda ctx, value: f(ctx, None, value), f)

그 다음엔 다음처럼 작성하면 된다.

@compatcallback
def callback(ctx, param, value):
    return value.upper()

클릭 1.0에서는 매개변수를 전달하지 않으므로 param 인자가 None이 될 것이고, 따라서 호환 콜백에서 그 인자를 사용할 수 없을 것이다.