Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 601 – finally에서 return/break/continue로 빠져나오는 것을 금지합니다.

Author:
Damien George, Batuhan Taskaya
Sponsor:
Alyssa Coghlan
Discussions-To:
Discourse thread
Status:
Rejected
Type:
Standards Track
Created:
26-Aug-2019
Python-Version:
3.8
Post-History:
26-Aug-2019, 23-Sep-2019
Superseded-By:
765
Resolution:
Discourse message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

거부 기록

이 PEP는 운영 위원회의 4/4 투표로 거부되었습니다.

Guido가 이 PEP를 거부한 근거는 다음과 같습니다. “대부분의 언어가 이런 종류의 구문을 구현하지만 이를 거부하는 스타일 가이드 및/또는 린터를 둔 것으로 보입니다. 이를 PEP 8에 추가하는 제안을 지지하겠습니다.” 그리고 “장난감 예제는 다소 오해의 소지가 있다고 생각합니다. 유용할 수 있는 기능은 finally 블록 내부의 조건부 return(또는 break 등)입니다.”라고 했습니다.

개요

이 PEP는 finally스위트에서 빠져나오게 되는 경우 해당 finally스위트 내부의 return, breakcontinue 문을 금지할 것을 제안합니다. 이러한 위치에서 이를 사용하면 finally를 거쳐 발생 중인 활성 예외가 조용히 취소되어, 불명확한 코드와 잠재적인 버그가 발생합니다.

현재 Python 3.7에서는 구현상의 문제로 finally에서 continue를 지원하지 않으며, Python 3.8에서도 이를 지원하지 말자는 것이 제안의 내용입니다. returnbreak에 대해서는 Python 3.9에서 사용을 폐지하고, Python 3.10에서 컴파일 경고를 발생시킨 다음 그 이후에는 사용을 금지하자는 것이 제안의 내용입니다.

동기

finally스위트 안에서 return, breakcontinue를 사용하면 전혀 명확하지 않은 동작이 발생합니다. 다음 함수를 살펴보십시오.:

def foo():
    try:
        foo()
    finally:
        return

이 함수는 무한 재귀를 수행하고 try내부에서 예외를 발생시키는데도 예외 없이 정상적으로 반환합니다. 그 이유는 finally내부의 returnfinally스위트를 통해 전파되는 모든 예외를 조용히 취소하기 때문입니다. 이러한 동작은 예상하기 어렵고 전혀 명확하지 않습니다. 이 함수는 다음과 동등합니다.:

def foo():
    try:
        foo()
    except:
        pass
    return

breakcontinuefinally스위트 밖의 코드로 이동하면 유사하게 동작하여 예외를 무시합니다. 예를 들어 다음과 같습니다.:

def bar():
    while True:
        try:
            1 / 0
        finally:
            break

이러한 동작은 Python의 선 다음 부분에 어긋납니다.

  • 명시적인 것이 암시적인 것보다 낫습니다. 예외가 암시적으로 무시됩니다.
  • 가독성이 중요합니다. 코드의 의도가 명확하지 않습니다.
  • 오류가 조용히 전달되어서는 안 됩니다. 명시적으로 무시하지 않는 한 예외가 암시적으로 무시됩니다.

예외를 무시하는 이러한 동작이 정말 필요하다면 명시적인 try-except 형식을 대신 사용할 수 있으며, 이렇게 하면 코드가 더 명확해집니다.

의미론과는 별개로, finally스위트 안에서 return/break/continue를 구현하는 일은 실행 중인 모든 활성 예외를 런타임에 정확히 추적하고 적절히 취소해야 하므로 쉽지 않습니다(실행 중인 finally스위트에 활성 예외가 있을 수도 있고 없을 수도 있습니다). CPython은 continue의 경우 이와 관련된 버그가 있었으므로 처음에는 이를 허용하지 않았습니다 [1]. finally스위트 안에서 return/break/continue가 올바르게 동작하도록 요구하는 것은 Python의 대체 구현에 불필요한 부담을 줍니다.

다른 언어

Java에서는 finally블록 안에서 반환할 수 있지만, [2], [3], [4]에 따르면 이러한 사용은 권장되지 않습니다. 이후 Java 컴파일러에는 -Xlint:finally 린팅 옵션이 추가되어 finally블록 안에서 return을 사용하는 것에 대해 경고합니다. Eclipse 편집기도 이러한 사용에 대해 경고합니다.

Ruby는 ensure(파이썬의 finally에 해당) 내부에서의 return을 허용하지만, 명시적인 return이어야 합니다. 이는 권장되지 않으며 린터에 의해 처리됩니다 [5], [6].

Ruby와 마찬가지로 JavaScript도 finally내에서 return/break/continue의 사용을 허용하지만, 이는 안전하지 않은 것으로 간주되어 eslint에 의해 처리됩니다 [7].

C#은 finally내에서 return/goto/break와 같은 종료문의 사용을 금지합니다 [8], [9].

근거

finally 내에서의 return/break/continue 동작이 불분명하고, 이 패턴은 거의 사용되지 않으며, 동등한 코드를 작성하는 더 명시적인 간단한 대안이 존재하므로, 이 구문을 금지하는 것이 가장 직관적인 접근 방식입니다.

명세

이는 문법이 아닌 컴파일러에 대한 변경입니다. 컴파일러는 finally 스위트에서 다음을 검사해야 합니다:

  • 임의의 중첩 수준에 있는 임의의 문장 내의 return.
  • finally 스위트 밖으로 제어 흐름을 전달하게 되는, 임의의 중첩 수준에 있는 임의의 문장 내의 break/continue.

이러한 경우를 발견하면 적절한 예외를 발생시켜야 합니다:

  • continue의 경우, SyntaxError (이는 3.7의 현재 동작입니다).
  • return/break의 경우, 3.10에서는 SyntaxWarning, 그 이후에는 SyntaxError입니다.

예를 들어, 다음은 모두 이 제안에 의해 금지됩니다:

def f():
    try:
        pass
    finally:
        return

def g():
    try:
        pass
    finally:
        try:
            return
        finally:
            pass

def h():
    try:
        pass
    finally:
        try:
            pass
        finally:
            for x in range(10):
                return

다음은 continuefinally를 벗어나지 않으므로 여전히 허용됩니다:

try:
    pass
finally:
    for x in range(10):
        continue

finally내에서 yield하는 것은 이 PEP에서도 여전히 허용된다는 점에 유의하십시오. 제너레이터를 재개하면 finally도 재개되어 결국 활성 예외가 있다면 이를 발생시키기 때문입니다(따라서 yield에 의해 예외가 무시되는 일은 없습니다).

하위 호환성

이는 returnbreak에 대한 하위 호환성이 없는 변경입니다.

CPython 표준 라이브러리(v3.8.0b1-651-g7fcc2088a5 기준)의 다음 위치에서는 finally내에서 return을 사용합니다:

  • Lib/subprocess.py:921 - 여기서의 사용은 버그처럼 보입니다
  • Lib/multiprocessing/connection.py:316 - 여기서의 사용은 정당해 보이지만 의도가 명확하지 않습니다
  • Lib/multiprocessing/connection.py:318 - 여기서의 사용은 정당해 보이지만 의도가 명확하지 않습니다
  • Lib/test/test_sys_settrace.py:837 - finally내에서의 return에 대한 테스트
  • Lib/test/test_sys_settrace.py:1346 - finally내에서의 return에 대한 테스트

표준 라이브러리에는 finally내에서 break를 사용하는 사례(finally를 벗어나는 경우)가 없습니다.

보안 영향

이는 언어의 단순화이자 관련 코드의 제거이므로, 보안 취약점 악용을 위한 새로운 경로를 도입하지 않을 것입니다.

이를 가르치는 방법

이 기능은 매우 드물게 사용되므로 이를 금지하더라도 초보자나 기존 교육 자료보다는 고급 사용자에게만 영향을 미칠 가능성이 큽니다. 이는 기능의 제거이므로, 금지된 기능이 사용될 경우 SyntaxError가 발생하는 것 자체가 사용자에게 이를 가르치는 방법이 될 것입니다.

참조 구현

현재는 참조 구현이 존재하지 않지만, finally에서 continue가 현재 처리되는 방식(SyntaxError 발생)을 returnbreak로 확장할 수 있습니다.

참고 자료