코드베이스가 커지면서 API를 호출하는 지점 수가 많아지거나 여러 소스 코드 저장소에 호출 지점이 흩어지면, API변경과 호출 지점 변경을 함께 일관성 있게 수행하는 것이 어려울 수 있다.
그러므로, 자신의 코드를 리팩터링하고 여러분의 API를 사용하는 부분을 최신 API에 맞춰 변경하도록 협력을 요청할 수 있는 방법을 찾아야한다.
키워드 인자를 지정하여 함수 호출을 명확하게 만들라
#키에 해당하는 단위로 돼 있는 값을
#SI 단위계 단위로 바꿀 때 곱해야 하는 숫자를 저장하는 딕셔너리
CONVERSIONS = {
'mph': 1.60934 / 3600 * 1000, # 마일/초 -> 미터/초
'시간': 3600, #시간 -> 초
'마일': 1.60934 * 1000, # 마일 -> 미터
'미터': 1,
'm/s': 1,
'초':1
}
def convert(value, units):
rate = CONVERSIONS[units]
return rate * value
def localize(value, units):
rate = ONVERSIONS[units]
return value / rate
def print_distance(speed, duration, *,
speed_units = 'mph',
time_units = '시간',
distance_units = '마일'):
norm_speed = convert(speed, speed_units)
norm_duration = convert(duration, time_units)
norm_distance = norm_speed * norm_duration
distance = localize(norm_distance, distance_units)
print(f{'distance} {distance_units}')
"""
단위를 마일로 변환을 해도 정확하게 표시하도록 만들 수 있다
"""
print_distance(1000, 3,
speed_units = '미터',
time_units = '초')
#1.8641182099494205
단위를 지정하는 것은 좋은 방향이고, 오류 가능성이 낮아지긴 하지만 기존 API를 호출하는 모든 사용자가 항상 단위를 지정할 수는 없을 것이다.
함수를 호출하는 쪽의 코드에 가능한 한 빨리 새로운 단위 인자를 포함시키도록 장려할 수 있다.
자신이 의존하는 모드가 변경됐으므로 각자의 코드를 변경하라고 안내할 수 있다.
컴퓨터가 자동으로 오류를 처리 시 주로 예외를 사용하지만 협업을 할 땐 의사를 전달할 때는 경고를 사용한다.
"""
warnings.warn 함수는 stacklevel 파라미터를 지원하고, 호출 스택에서 경고를 발생시킨 위치를 제대로 보고할 수 있다.
stacklevel을 활용하면 다른 코드를 대신해 경고를 표시하는 함수를 쉽게 작성할 수 있기에 준비 코드를 줄일 수 있다.
선택적인 인자가 제공되지 않은 경우에 경고를 표시하고 빠진 인자에 대한 디폴트 값을 제공해주는 도우미 함수를 정의한다.
"""
def require(name, value, default):
if value is not None:
return value
warnings.warm)
f'{name}이(가) 곧 필수가 됩니다. 코드를 변경해 주세요',
DeprecationWarning,
stacklevel=3)
return default
def print_distance(speed, duration, *,
speed_units=None,
time_units = None,
distance_units = None):
speed_units = require('speed_units', speed_units, 'mph')
time_units = require('time_units', time_units,'시간')
distance_units = require(
'distance_units', distance_units, '마일')
norm_speed = convert(speed, speed_units)
norm_duration = convert(duration, time_units)
norm_distance = norm_speed * norm_duration
distance = localize(norm_distance, distance_units)
print(f'{distance} {distance_units}')
#프로그램을 실행한 결과를 검사하면 이 함수가 경고할 대상 위치를 제대로 전달하는지 확인할 수 있다.
import contextlib
import io
fake_stderr = io.StringIO()
with contextlib.redirect_stderr(fake_stderr):
print_distance(1000,3,
speed_units = '미터',
time_units = '초')
warnings.simplefilter('error')
try:
warnings.warn('이 사용법은 향후 금지될 예정입니다',
DeprecationWarning)
except DeprecationWarning:
print('DeprecationWarning이 예외로 발생")
pass #에외가 발생할 것으로 예상함
print(fake_stderr.getvalue())
#1.8641182099494205 마일
#DeprecationWarning이 예외로 발생```
코드를 입력하세요
<br>
>예외를 발생시키는 동작 방식은 상위 의존 관계에 있는 변경을 감지해서 적절히 실패하는 자동화된 테스트에 유용하고, 협업하는 사람들에게 코드를 변경해야 한다는 사실을 명확히 알려주는 좋은 방법이다.
"""
warnings.simplefilter('error)를 쓰지 않아도, -W error 명령줄 인자를 파이썬 인터프리터에게 넘기거나
PYTHONWARNINGS 환경 변수를 설정해서 이런 정책을 사용할 수 있다.
"""
#ex6.py
import warnings
try:
warnings.warn('이 사용법은 향후 금지될 예정입니다.',
DeprecationWarning)
except DeprecationWarning:
print("DeprecationWarning이 예외로 발생")
#실행
$ python -W error ex6.py
DeprecationWarning이 예외로 발생
로깅을 사용하여 경고를 잡아낼 경우, 프로그램에 오류 보고 시스템이 설정된 경우 프로덕션 환경에서도 중요한 경고를 통보받을 수 있다.
import logging
fake_stderr = io.StringIO()
handler = logging.StreamHandler(fake_stderr)
formatter = logging.Formatter(
'%(asctime) -15s WARNING] %(message)s')
handler.setFormatter(formatter)
logging.captureWarnings(True)
logger = logging.getLogger('py.warnings')
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
warnings.resetwarnings()
warnings.simplefilter('default')
warnings.warn('이 경고는 로그 출력에 표시됩니다.')
print(fake_stderr.getvalue())
#결과
2020-08-29 23:15:43,465 WARNING] ex8.py:17: UserWarning: 이 경고는 로그 출력에 표시됩니다.
warnings.warn('이 경고는 로그 출력에 표시됩니다.')
API 라이브러리 관리자는 경고가 제대로 된 환경에서 명확하고 해결 방법을 제대로 알려주는 메시지와 함께 만들어지는지 검증하는 단위 테스트를 작성해야한다.
with warnings.catch_warnings(record=True) as found_warnings:
found = require('my_arg', None, '가짜 단위')
expected = '가짜 단위'
assert found ==expected
"""
경고 메시지를 수집하고 나면 경고의 개수, 자세한 메시지, 분류가 예상과 맞아떨어지는지 확인
"""
assert len(found_warnings) == 1
single_warning = found_warnings[0]
assert str(single_warning.message) == (
'my_arg이(가) 곧 필수가 됩니다. 코드를 변경해 주세요')
assert single_warning.category == DeprecationWarning
warnings 모듈을 사용하면 여러분의 API를 호출하는 사용자들에게 앞으로 사용 금지될 사용법에 대해 알려줄 수 있고, 경고 메시지는 API 사용자들이 자신의 코드가 깨지기 전에 코드를 변경하도록 권장한다.
-w error 명령줄 인자를 파이썬 인터프리터에게 넘기면 경고를 오류로 높일 수 있고, 의존 관계에서 잠재적인 회귀 오류가 있는지 잡아내고 싶은 자동화 테스트에서 기능이 특히 유용하다.
프로덕션 환경에서는 경고를 logging 모듈로 복제해 실행 시점에 기존 오류 보고 시스템이 경고를 잡아내게 할 수 있다.
다운스트림 의존 관계에서 알맞은 경고가 발동되도록 코드가 생성하는 경고에 대해 테스트를 작성하면 유용하다.