# 2to3 — Automated Python 2 to 3 code translation

2to3는 파이썬 2.x 소스 코드를 유효한 파이썬 3.x 코드로 변환하기 위해 일련의 변환자(fixers)를 적용하는 프로그램입니다. 표준 라이브러리는 많은 양의 변환자를 제공하고 있어 코드 대부분을 처리할 수 있을 것입니다. 2to3에서 사용하는 모듈인 `lib2to3` 는 유연하고 제네릭합니다. 따라서 2to3 프로그램을 위해 당신만의 변환자를 작성할 수 있습니다.

## 2to3 사용법

파이썬 인터프리터가 설치될 때, 보통 2to3 스크립트도 같이 설치됩니다. 2to3 스크립트 파일은 파이썬 루트 디렉터리의 하위 디렉터리인 `Tools/scripts` 에서 찾을 수 있습니다.

2to3의 기본 인자는 변환하고자 하는 파일이나 디렉터리 리스트입니다. 디렉터리의 경우 하위 폴더의 파이썬 소스까지 적용됩니다.

샘플 파이썬 2.x 코드가 여기 있습니다. `example.py`:

    def greet(name):
        print "Hello, {0}!".format(name)
    print "What's your name?"
    name = raw_input()
    greet(name)

명령줄에서 2to3를 실행하면 이 코드를 파이썬 3.x 코드로 바꿀 수 있습니다:

    $ 2to3 example.py

원본 파일과 변환 결과를 비교한 차이점(diff)이 출력됩니다. 2to3은 원본 소스 파일에 필요한 수정사항을 바로 적용할 수도 있습니다. (`-n` 옵션이 적용되지 않았다면 원본 파일에 대한 백업이 생성될 것입니다.) `-w` 옵션을 사용하면 바로 원본 파일이 수정됩니다.

    $ 2to3 -w example.py

`example.py` 를 변환한 결과는 다음과 같습니다.

    def greet(name):
        print("Hello, {0}!".format(name))
    print("What's your name?")
    name = input()
    greet(name)

변환 과정에서 들여쓰기와 주석은 그대로 보존됩니다.

기본적으로 2to3는 미리 정의된 변환자 를 사용하여 실행됩니다. `-l` 옵션을 사용하면 사용 가능한 모든 변환자를 볼 수 있습니다. 특정 변환자만 명시적으로 설정하고 싶으시면 `-f` 를 사용하시면 됩니다. 마찬가지로 `-x` 옵션으로 특정 변환자를 비활성화할 수도 있습니다. 다음 예는 `imports` 와 `has_key` 변환자만 사용한 것입니다.

    $ 2to3 -f imports -f has_key example.py

다음은 `apply` 변환자만 빼고 모든 변환자를 실행하는 명령어입니다.

    $ 2to3 -x apply example.py

몇몇 변환자는 기본적으로 실행되지 않기 때문에 명시적으로 명령줄에서 설정해야 합니다. 기본 변환자에 `idioms` 변환자를 추가한 예시가 여기 있습니다.

    $ 2to3 -f all -f idioms example.py

모든 기본 변환자를 활성화하기 위해 `all` 값을 사용한 것을 주목해주세요.

때때로 2to3는 자동 변환을 하지 못하고 당신의 코드에서 수정이 필요한 부분을 찾을 수도 있습니다. 이러면 파일의 비교 결과 아래에 경고 문구를 출력할 것입니다. 당신은 이 소스코드를 3.x 버전에 맞도록 경고 사항을 수정해야 합니다.

2to3는 doctest도 수정할 수 있습니다. 이것을 활성화하기 위해서는 `-d` 옵션을 사용하세요. 이것은 오직 doctest만 수정한다는 것을 명심하세요. 이것을 사용하기 위해서 꼭 적합한 파이썬 모듈이 필요한 것은 아닙니다. 예를 들어 reST 문서에 있는 예와 같은 doctest도 이 옵션과 함께 수정할 수 있습니다.

`-v` 옵션은 변환 과정 동안 더 자세한 정보를 출력하게 해줍니다.

어떤 print 문장의 경우는 문장 또는 함수 호출로 파싱될 수 있기 때문에 2to3이 print 함수를 포함한 파일을 항상 처리할 수 있는 것은 아닙니다. 2to3이 `from __future__ import print_function` 이란 컴파일러 지시어를 찾았다면 2to3는 `print()` 를 함수로 처리하도록 내부 처리 문법을 변경할 것입니다. 이러한 변경은 `-p` 옵션을 가지고 직접 활성화할 수도 있습니다. 출력 문장이 이미 변경된 코드에 변환자를 실행하기 위해 `-p` 옵션을 사용하세요. 또한 `-e`를 사용하여 `exec()`를 함수로 만들 수 있습니다.

`-o` 또는 `--output-dir` 옵션을 사용하면 출력 파일이 쓰일 디렉터리를 설정할 수 있습니다. `-n` 옵션은 입력 파일을 덮어쓰지 않아 백업 파일이 필요 없을 때 사용할 수 있습니다.

버전 3.2.3에 추가: `-o` 옵션이 추가되었습니다.

`-W` 또는 `--write-unchanged-files` 옵션은 변경 사항이 없더라도 항상 출력 파일을 쓰도록 합니다. 이것을 `-o` 옵션과 함께 쓰면 한 디렉터리에 있는 전체 파이썬 소스 트리를 파이썬 3.x로 변환해서 다른 디렉터리로 복사할 때 유용하게 사용할 수 있습니다. 이치에 맞게 하기 위해 이 옵션은 `-w` 옵션을 포함하고 있습니다.

버전 3.2.3에 추가: `-W` 옵션이 추가되었습니다.

`--add-suffix` 옵션은 모든 출력 파일 이름 뒤에 추가할 문자열을 지정합니다. 다른 파일 이름으로 저장할 때 백업 파일이 필요하지 않다면 `-n` 옵션을 같이 사용해야 합니다. 예시:

    $ 2to3 -n -W --add-suffix=3 example.py

이 명령어는 출력 파일의 이름을 `example.py3` 로 만들어 줍니다.

버전 3.2.3에 추가: `--add-suffix` 옵션이 추가되었습니다.

한 디렉터리에서 다른 디렉터리로 전체 프로젝트를 변환하고 싶을 때는 다음과 같이 하면 됩니다.

    $ 2to3 --output-dir=python3-version/mycode -W -n python2-version/mycode

## 변환자 목록

변환되는 코드의 각각 단계는 변환자 안에 캡슐화되어 있습니다. `2to3 -l` 명령어를 실행하면 변환자 목록을 보실 수 있습니다. 위에 적어놓은 것 과 같이, 각 변환자는 개별적으로 활성화/비활성화를 할 수 있습니다. 변환자들에 대한 자세한 설명이 아래에 있습니다.

`apply`

  `apply()` 사용을 제거합니다. 예를 들어 `apply(function, *args, **kwargs)` 를 `function(*args, **kwargs)` 로 변경합니다.

`asserts`

  폐지된 `unittest` 메서드 이름을 올바른 것으로 변경합니다.

  변경 전 | 변경 후
  --- | ---
  `failUnlessEqual(a, b)` | `assertEqual(a, b)`
  `assertEquals(a, b)` | `assertEqual(a, b)`
  `failIfEqual(a, b)` | `assertNotEqual(a, b)`
  `assertNotEquals(a, b)` | `assertNotEqual(a, b)`
  `failUnless(a)` | `assertTrue(a)`
  `assert_(a)` | `assertTrue(a)`
  `failIf(a)` | `assertFalse(a)`
  `failUnlessRaises(exc, cal)` | `assertRaises(exc, cal)`
  `failUnlessAlmostEqual(a, b)` | `assertAlmostEqual(a, b)`
  `assertAlmostEquals(a, b)` | `assertAlmostEqual(a, b)`
  `failIfAlmostEqual(a, b)` | `assertNotAlmostEqual(a, b)`
  `assertNotAlmostEquals(a, b)` | `assertNotAlmostEqual(a, b)`

`basestring`

  `basestring` 을 `str` 로 변환합니다.

`buffer`

  `buffer` 를 `memoryview` 로 변환합니다. `memoryview` API가 `buffer` API와 비슷하긴 하지만 완전히 같진 않아서 이 변환자는 선택적으로 실행됩니다.

`dict`

  딕셔너리 이터레이션 메서드를 변환합니다. `dict.iteritems()` 를 `dict.items()` 로, `dict.iterkeys()` 를 `dict.keys()` 로, `dict.itervalues()` 를 `dict.values()` 로 변경합니다. 마찬가지로 `dict.viewitems()`, `dict.viewkeys()`, `dict.viewvalues()` 를 각각 `dict.items()`, `dict.keys()`, `dict.values()` 로 변경합니다. 기존의 `dict.items()`, `dict.keys()`, `dict.values()` 의 사용을 `list` 로 감싸도록 바꿉니다.

`except`

  `except X, T` 를 `except X as T` 로 변환합니다.

`exec`

  `exec` 문장을 `exec()` 함수로 변환합니다.

`execfile`

  `execfile()` 사용을 제거합니다. `execfile()` 에 사용되는 인자는 `open()`, `compile()`, `exec()` 을 사용하도록 바뀝니다.

`exitfunc`

  `sys.exitfunc` 대입이 `atexit` 모듈을 사용하도록 바뀝니다.

`filter`

  `filter()` 함수 사용을 `list` 로 감싸도록 바꿉니다.

`funcattrs`

  이름이 변경된 함수 어트리뷰트를 변환합니다. 예를 들어 `my_function.func_closure` 를 `my_function.__closure__` 로 변경합니다.

`future`

  `from __future__ import new_feature` 구문을 제거합니다.

`getcwdu`

  `os.getcwdu()` 를 `os.getcwd()` 로 변경합니다.

`has_key`

  `dict.has_key(key)` 를 `key in dict` 로 바꿉니다.

`idioms`

  이 선택적인 변환자는 이디엄을 더 사용하도록 파이썬 코드를 변환해줍니다. `type(x) is SomeClass` 나 `type(x) == SomeClass` 같은 형 비교는 `isinstance(x, SomeClass)` 로 변환합니다. `while 1` 는 `while True` 로 변환합니다. 또한 이 변환자는 `sorted()` 가 올바른 위치에 사용될 수 있도록 수정합니다. 예를 들어 다음 코드는

      L = list(some_iterable)
      L.sort()

  아래와 같이 변경됩니다.

      L = sorted(some_iterable)

`import`

  같은 단계 경로의 임포트를 찾아 상대 경로 임포트로 변경합니다.

`imports`

  표준 라이브러리에 있는 모듈의 이름 변경 사항을 처리합니다.

`imports2`

  표준 라이브러리에 있는 또 다른 모듈의 이름 변경 사항을 처리합니다. 기술적인 제한 사항 때문에 `imports` 변환자와 분리했습니다.

`input`

  `input(prompt)` 를 `eval(input(prompt))` 로 변경합니다.

`intern`

  `intern()` 를 `sys.intern()` 로 변경합니다.

`isinstance`

  `isinstance()` 의 두 번째 인자에서 중복된 형을 수정합니다. 예를 들어 `isinstance(x, (int, int))` 를 `isinstance(x, int)` 로, `isinstance(x, (int, float, int))` 를 `isinstance(x, (int, float))` 로 변경합니다.

`itertools_imports`

  `itertools.ifilter()`, `itertools.izip()`, `itertools.imap()` 임포트를 제거합니다. `itertools.ifilterfalse()` 임포트를 `itertools.filterfalse()` 로 바꿉니다.

`itertools`

  `itertools.ifilter()`, `itertools.izip()`, `itertools.imap()` 를 각각 그것에 맞는 내장 함수로 변경합니다. `itertools.ifilterfalse()` 를 `itertools.filterfalse()` 로 변경합니다.

`long`

  `long` 을 `int` 로 바꿉니다.

`map`

  `map()` 을 `list` 로 감싸도록 바꿉니다. `map(None, x)` 를 `list(x)` 로 바꿉니다. `from future_builtins import map` 를 사용하면 이 변환자가 비활성화됩니다.

`metaclass`

  구식의 메타 클래스 문법(클래스 바디에 `__metaclass__ = Meta` 를 사용)을 새로운 문법(`class X(metaclass=Meta)`)으로 변경합니다.

`methodattrs`

  구식의 메서드 어트리뷰트 이름을 수정합니다. 예를 들어 `meth.im_func` 를 `meth.__func__` 로 변경합니다.

`ne`

  구식의 부등호 문법인 `<>` 을 `!=` 로 변경합니다.

`next`

  이터레이터의 `next()` 메서드 사용을 `next()` 함수로 변경합니다. 또한 `next()` 메서드를 `__next__()` 로 바꿉니다.

`nonzero`

  Renames definitions of methods called `__nonzero__()` to `__bool__()`.

`numliterals`

  8진수 리터럴을 새 문법으로 변경합니다.

`operator`

  `operator` 모듈에 있는 다양한 함수 호출을 그것에 대응하는 다른 함수 호출로 변경합니다. `import collections.abc` 와 같이 필요하다면 `import` 구문도 추가됩니다. 다음과 같이 변경합니다.

  변경 전 | 변경 후
  --- | ---
  `operator.isCallable(obj)` | `callable(obj)`
  `operator.sequenceIncludes(obj)` | `operator.contains(obj)`
  `operator.isSequenceType(obj)` | `isinstance(obj, collections.abc.Sequence)`
  `operator.isMappingType(obj)` | `isinstance(obj, collections.abc.Mapping)`
  `operator.isNumberType(obj)` | `isinstance(obj, numbers.Number)`
  `operator.repeat(obj, n)` | `operator.mul(obj, n)`
  `operator.irepeat(obj, n)` | `operator.imul(obj, n)`

`paren`

  리스트 컴프리헨션 안에 괄호가 필요한 경우 추가합니다. 예를 들어 `[x for x in 1, 2]` 를 `[x for x in (1, 2)]` 로 변경합니다.

`print`

  `print` 구문을 `print()` 함수로 변경합니다.

`raise`

  `raise E, V` 를 `raise E(V)` 로, `raise E, V, T` 를 `raise E(V).with_traceback(T)` 로 변경합니다. 만약 `E` 가 튜플인 경우, 변환된 결과물은 동작하지 않을 것입니다. 왜냐하면 튜플이 예외를 대체하는 것은 3.0부터 사라졌기 때문입니다.

`raw_input`

  `raw_input()` 를 `input()` 로 변경합니다.

`reduce`

  `reduce()` 를 `functools.reduce()` 로 변경합니다.

`reload`

  `reload()` 를 `importlib.reload()` 로 변경합니다.

`renames`

  `sys.maxint` 를 `sys.maxsize` 로 변경합니다.

`repr`

  백틱 repr을 `repr()` 함수로 바꿉니다.

`set_literal`

  `set` 생성자를 집합 리터럴로 바꿉니다. 이 변환자는 선택적입니다.

`standarderror`

  `StandardError` 를 `Exception` 로 바꿉니다.

`sys_exc`

  더 사용되지 않을 `sys.exc_value`, `sys.exc_type`, `sys.exc_traceback` 를 `sys.exc_info()` 로 변경합니다.

`throw`

  제너레이터 `throw()` 메서드의 API 변경 사항을 반영합니다.

`tuple_params`

  묵시적으로 튜플 매개 변수를 언 패킹하는 것을 제거합니다. 이로 인해 이 변환자는 임시 변수를 추가합니다.

`types`

  `types` 모듈에서 몇몇 멤버가 삭제되어 코드가 동작하지 않던 것을 수정합니다.

`unicode`

  `unicode` 를 `str` 로 변경합니다.

`urllib`

  `urllib` 와 `urllib2` 를 `urllib` 패키지로 변경합니다.

`ws_comma`

  쉼표로 구분뒨 아이템 목록에서 필요 이상의 공백을 제거합니다. 이 변경자는 선택적입니다.

`xrange`

  `xrange()` 를 `range()` 로 바꿉니다. 기존 `range()` 를 `list` 로 감쌉니다.

`xreadlines`

  `for x in file.xreadlines()` 를 `for x in file` 로 변경합니다.

`zip`

  `zip()` 를 `list` 로 감쌉니다. 이 변환자는 `from future_builtins import zip` 가 있을 때는 비활성화됩니다.

## `lib2to3` — 2to3’s library

소스 코드: Lib/lib2to3/

버전 3.11에서 폐지되었습니다, 버전 3.13에서 제거됩니다.: Python 3.9 switched to a PEG parser (see PEP 617) while lib2to3 is using a less flexible LL(1) parser. Python 3.10 includes new language syntax that is not parsable by lib2to3’s LL(1) parser (see PEP 634). The `lib2to3` module was marked pending for deprecation in Python 3.9 (raising `PendingDeprecationWarning` on import) and fully deprecated in Python 3.11 (raising `DeprecationWarning`). It will be removed from the standard library in Python 3.13. Consider third-party alternatives such as LibCST or parso.

참고

`lib2to3` API는 미래에 크게 바뀔 수 있기 때문에 안정적이지 않다고 생각해야 합니다.