【Python2to3アップデート方法】futurizeで安全にPython2系コードを3系用に自動変換

## 目次
- [【Python2to3アップデート方法】目次](https://qiita.com/Esfahan/items/067484ee0cb3dd374992)

## コードをPython 2系から3系用に自動変換する
Python2系のコードを3系用に自動変換してくれるツールである**futurize**の使い方について説明する。

## この記事を読む前に
コード変換ツールはいくつかあり、それぞれ特徴が異なる。
まずは自分の環境にどのツールが合うのか、以下の記事を読んで判断することをオススメする。

- [【Python2to3アップデート方法】2to3 vs futurize どちらでPythonコード変換をすればよいのか](https://qiita.com/Esfahan/items/b9fd1e1ff2d599a651e7)

## stageについて
futurizeを理解するためには、stageについて理解しておく必要がある。
futurizeのコード変換方式には、stage1と2がある。

### stage1
stage1は、Python2系環境でも影響のない範囲でのコード変換を行う。そのため、stage1での変換後コードは、Python3系環境では動かない可能性がある。stage1の目的は、Python3系用にコードを完全に変化する前段階として、まずはPython2で動く範囲でのコード変換を行うことである。stage1を挟むことにより、一気にコードを変換するリスクを和らげることができる。

stage1では、\_\_future\_\_モジュールを利用し、Python3系にあって2系では互換性がない機能でも、2系の環境で実装できるようにする。futureモジュールは追加しない。

### stage2
stage2では、Python3系用に完全にコードを書き換える。ただし、Python2系でも動く内容で安全に変換を行うため、[2to3を使った場合とは実装内容が異なる](【Python2to3アップデート方法】2to3 vs futurize どちらでソースコード変換をすればよいのか)。stage2では、futureモジュールを追加する。

## 事前準備
まずは検証用に、2系では動くが3系では動かないソースコードを作成する。
以下の記事の「事前準備」の項を参考して作成してください。

- [事前準備](https://qiita.com/Esfahan/items/22d38bce43277db31972#事前準備)

## futurizeでコードを変換
### Installation
futurizeコマンドを利用するには、pipモジュールのインストールが必要。

```
$ pip install future
```

## Usage
### stage1
まずは、stage1でコードの変更が必要な箇所をチェックする。
この段階ではまだdry-runであり、コードの変換は行われない。

```diff
$ futurize --stage1 project
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: ws_comma
RefactoringTool: Refactored project/base.py
--- project/base.py	(original)
+++ project/base.py	(refactored)
@@ -1,4 +1,5 @@
 # -*- coding:utf-8 -*-
+from __future__ import print_function
 import libs.sample_class as sc


@@ -6,5 +7,5 @@
     '''
     It should be `print()` with Python3.x
     '''
-    print 'This is a sample.\n'
+    print('This is a sample.\n')
     sc.SampleClass().sample_method()
RefactoringTool: No changes to project/libs/base.py
RefactoringTool: Refactored project/libs/sample_class.py
--- project/libs/sample_class.py	(original)
+++ project/libs/sample_class.py	(refactored)
@@ -4,7 +4,9 @@
 - `import PRJ_ROOT/base.py` with Python2.x
 - `import PRJ_ROOT/libs/base.py` with Python3.x
 '''
-import base
+from __future__ import print_function
+from __future__ import absolute_import
+from . import base
 '''
 It should be `configparser` with Python3.x
 '''
@@ -17,11 +19,11 @@
         '''
         It should be `print()` with Python3.x
         '''
-        print '[division]'
+        print('[division]')
         '''
         # Results
         - `0` with Python 2.x
         - `0.5` with Python 3.x
         '''
-        print '1/2 = {}'.format(1/2)
-        print '1//2 = {}'.format(1//2)
+        print('1/2 = {}'.format(1/2))
+        print('1//2 = {}'.format(1//2))
RefactoringTool: Files that need to be modified:
RefactoringTool: project/base.py
RefactoringTool: project/libs/base.py
RefactoringTool: project/libs/sample_class.py
```

### コード自動変換
以下のコマンドで、実際にコードを書き換える。

`-w`オプションを付けることで、ファイルを上書きして書き換えてくれる。
実行すると、バックアップファイルも作成される。

```
$ futurize --stage1 -w project
```

`--nobackups`オプションを付けると、バックアップファイルは作成されない。

```
$ futurize --stage1 -w --nobackups project
```

### stage2
stage1での変換後に実施する。
次に、stage2でコードの変更が必要な箇所をチェックする。
この段階ではまだdry-runであり、コードの変換は行われない。

```diff
$ futurize --stage2 project
RefactoringTool: Refactored project/libs/base.py
--- project/libs/base.py	(original)
+++ project/libs/base.py	(refactored)
@@ -1,11 +1,12 @@
 # -*- coding:utf-8 -*-
+from builtins import object
 from abc import ABCMeta, abstractmethod
+from future.utils import with_metaclass

-class Base(object):
+class Base(with_metaclass(ABCMeta, object)):
     '''
     The way of using Abstract differs between Python 2.x and 3.x
     '''
-    __metaclass__ = ABCMeta

     @abstractmethod
     def sample_method(self):
RefactoringTool: Refactored project/libs/sample_class.py
--- project/libs/sample_class.py	(original)
+++ project/libs/sample_class.py	(refactored)
@@ -6,11 +6,15 @@
 '''
 from __future__ import print_function
 from __future__ import absolute_import
+from __future__ import division
+from future import standard_library
+standard_library.install_aliases()
+from past.utils import old_div
 from . import base
 '''
 It should be `configparser` with Python3.x
 '''
-import ConfigParser
+import configparser


 class SampleClass(base.Base):
@@ -25,5 +29,5 @@
         - `0` with Python 2.x
         - `0.5` with Python 3.x
         '''
-        print('1/2 = {}'.format(1/2))
+        print('1/2 = {}'.format(old_div(1,2)))
         print('1//2 = {}'.format(1//2))
RefactoringTool: Files that need to be modified:
RefactoringTool: project/libs/base.py
RefactoringTool: project/libs/sample_class.py
```

### コードを自動変換
以下のコマンドで、実際にコードを書き換える。

`-w`オプションを付けることで、ファイルを上書きして書き換えてくれる。
実行すると、バックアップファイルも作成される。

```
$ futurize --stage2 -w project
```

`--nobackups`オプションを付けると、バックアップファイルは作成されない。

```
$ futurize --stage2 -w --nobackups project
```

#### コード自動変換後の3系での実行結果
書き換えを行ったコードを3系で実行してみる。

```shell-session
$ python base.py
This is a sample.

[division]
1/2 = 0
1//2 = 0
```

## 関連記事
- [【Python2to3アップデート方法】2to3でPython2系コードを3系用に自動変換](https://qiita.com/Esfahan/items/22d38bce43277db31972)

## 参考
- [Automatic conversion to Py2/3](http://python-future.org/automatic_conversion.html)