他人の書いたコードに挑もう – Part 1

この記事では、他人が書いたコードを扱うための練習法を一から説明します。目標は、 Spyder Python IDEという今まで触ったこともないプロジェクトのコードに任意の変更を加え、途中で行き詰ることなく、目的達成に必要な情報のみ習得することです。ここでは、勘や実験的な手段、そしてプロの現場で養った洞察力を武器に問題に対処する方法を学びます。形式ばったレッスンのように、苦痛を感じることはないでしょう。満足感や挫折、葛藤を味わいながらプロジェクトを進め、最終的には(なんとか動く程度の)パッチを完成させ、大規模で不慣れなコードベースに機能を追加します。

プログラミングを学んでいる人は皆、あらゆる種類のプログラムで大量のコードを書いています。それは、問題集に載っているアルゴリズムを実装するにせよ、ウェブサイトの構築やビデオゲームの作成をするにせよ同じです。ところがプロのソフトウェアエンジニアというのは、めったにコードをガリガリ書きません。それよりも他人のコードの解読に時間を割いています。解読するのは自分で書いたものでもなければ、今まで一度も見たこともないような、良く分からないコードです。助けを求める人や、心の叫びに気付いてくれる人がいなくても、前に進まなければならないのです。


目標

この記事の目標は、初見のコードベースの内容を調査して、ある程度インパクトのある修正を加えることです。この練習で使用するターゲットは、Spyder Python IDEで、Githubのページによると見た目は以下のような感じです。

Diving/Spyder.png
そして(任意の)目標は、Helpドロップダウンメニューの隣にMiscという名称のドロップダウンを新しく追加することです。その内部には、現在開いているテキスト領域を取得して、以下のコードを先頭に追加するメニューアイテムおよびコマンドを追加します。

print("Running %s" % __name__)

ファイルの実行開始時間を表示するため、一番上に挿入します。

私は職業柄、何年もPythonやJavaScriptを使ってきたので、これらのエコシステムについてはかなりの知識があります。Bashコマンドラインを使うLinuxの作業環境にも精通していて、オープンソース拡張も数多くこなしてきました。遠い昔にはJavaやC#、PHPもよく使っていました。ただ、Spyderエディタは一度も使ったことがなく、コードベースに触れるのも、実際に見るのも今日が初めてです。この練習では、このように何の知識もないコードベースを使い始める方法や、全てを把握せずとも前へ進む方法を模索します。

方針

以下は、未知のコードベースを扱う上での作業手順です。

以下は、他人のコードを扱う際の留意点です。

  • プロジェクトの内容を把握しましょう。今回扱うのは、Scientific Python用のグラフィカルなIDEの一種です。ありがたいことに、私はScientific Pythonの使用経験はありませんが、IDEとPythonは知っています。より専門的な使い方をするプロジェクトの場合は、着手前にその目的を把握しておきましょう。

  • 放棄されたプロジェクトや、長い間使用されていないプロジェクトでない限り、おそらくコードは動きます。Spyderの場合、最終コミットはごく最近です。そのコードで作業をして修正を加えた人がいるということは、コードが十分に動作する証しです。

  • コードを修正して動作確認をするため、何らかの方法で「開発」用にセットアップする必要があります。この点が「配布用に全てセットになったバンドル」版とは違うところでしょう。

  • 放棄されたプロジェクトや、一個人が作成したプロジェクト(その開発者のデスクトップ上にしかない特殊な環境を準備する必要がある場合)を除いて、セットアップは可能です。この記事で扱うのは、複数の人によって頻繁に開発されているプロジェクトなので、たとえセットアップに苦戦したとしても、それが可能であることは確かです。

  • セットアップに時間がかかっても大丈夫です。コードがゴチャゴチャに膨れ上がり、高度な設定を要する場合は、セットアップに「数日」かかるケースもあります。少し長いですが、珍しいことではありません。私が初めてPHPMySQLをインストールした時は9時間かかりました。理想的にはIPythonプロジェクトのように、数秒で開発環境をセットアップしたいところですが、そう都合良く単独で動作し、依存性がないプロジェクトばかりではありません。

明らかにする

プロジェクトの概要や動作が全く分からない状態で、手を加えようとするのは無謀です。なお、この記事で扱うのは、Spyder Python IDEです。

Diving/Spyder.png
Githubのページの概要にあるように、これはPython用にPythonを使用して書かれたPython IDEです。

Diving/Spyder.png
ご覧の通り、左側にはコードエディタ、右下にはIPython Notebookのようにグラフィカル出力に対応したIPython REPL(表示用のIn [1]シンタックスがあるのが分かります)、そして右上にはドキュメントブラウザがあります。それ以外は典型的なIDEと同じです。上部のボタンを使うと、コードの保存、ロード、コピー、実行、デバッグ、ステップスルーができます。ドロップダウンメニューには、さらに多くの機能があり、タブの付いたテキストエディタにはハイライトされたシンタックスなどが表示されています。

Spyderは一般的には、どのプラットフォーム上でも動作します。ドキュメントには、Windowsのほぼ全てのバージョンMac OS-XLinux(Ubuntu、Debian、Arch、Fedoraなど)用の説明が含まれています。

なかなか古そう(数年前のもの)に見えますが、非常に活発なプロジェクトで、たくさんの人に支えられています。

Diving/Contributors.png
営利目的の組織が支援しているのかは分かりませんが、個人向けの怪しげなプロジェクトではありません。

ここまでが、特に知識のない素人から見たSpyderの簡単な概要です。では次に、Macbook上でSpyderを動かしてみましょう。

インストール

試してみたいプロジェクトがある場合、まず最初にどのようにしてそれを「開発モード」でインストールするかを考えなければなりません。通常、プロジェクトには、プロジェクト内で作業するエンドユーザがダウンロードしやすい全てを含んだカプセル化された配布版があります。しかし、そのプロジェクトを基に作業する人が必ずしも使いたいバージョンとは限りまん。Spyderの場合、次の2つのセクションが区別されているようです。

ただIDEが欲しいエンドユーザであれば、インストールの手順に従います。しかし、Spyderを単に使うのではなく、変更を加えたいのでソースコードで実行を始めます。こちらの方がどんな時でも難しく、使いやすいとは言えない上、「パッケージ」された配布版よりも時間がかかります。いずれにせよ、コードに変更を加えてみたい人はこの困難をいつかは乗り越えなければならないので、さっさと片付けてしまいましょう。

どのプロジェクトにおいても共通することですが、まず依存性をダウンロードする必要があります。Spyderは外部の影響を受けやすく、依存性のセクションに次のような記述があります。

  • Python 2.7 あるいは3.3+。
  • PyQt5 5.2+あるいはPyQt4 4.6+:PyQt5が推奨です。
  • qtconsole: 強化されたPythonのインタプリタ。
  • RopeJedi: エディタコード補完、calltips機能やgo-to-definition機能。
  • Pyflakes: 即時コード解析。
  • Sphinx: ヘルプウィンドウのリッチテキストモード。
  • Pygments: シンタックスのハイライト機能が全てのファイルタイプに対応。
  • Pylint: 静的コード解析。
  • Pep8: スタイル解析。
  • Psutil: CPUおよびメモリ使用量のステータスバー。
  • Nbconvert: Editorでのノードブックの操作。
  • Qtawesome: FontAwesomeをベースにしたアイコンのテーマ
  • Pickleshare: Pythonのコンソールでインポートの完了を表示。
  • PyZMQ: 非同期でイントロスペクションサービスを実行。

とても長いリストです。Pythonが何かを理解していますが、それ以外の機能については把握できていません。大体においては、上の依存性を1つずつpipと呼ばれるPythonのパッケージマネージャを使ってインストールすることができます。しかし、依存性のセクションの最初に目に付く場所にちょっとした免責事項が記述されています。

  • 重要事項:下に列挙されている依存性全部ではありませんが、ほとんどはAnacondaやWinPython、Python(x,y)に付いてくるので、Scientific Pythonディストリビューションをインストールする時に別途これらをインストールする必要はありません。

1つの塊としてダウンロードしてインストールできるようです。個人的にこの「Scientific Pythonディストリビューション」を使ったことがありませんが、聞いたことはあります。これは、ダウンロードできるPythonパッケージで、使い勝手の良いライブラリなどが含まれたものだそうです。今回はSpyderがどのパッケージをダウンロードしても必要なものは入手できると教えてくれています。そのため、次のステップはどれかをダウンロードして試してみることです。まずはAnacondaを試してみましょう。

Anaconda

私はMacbook ProでOS-Xを使っています。「Anaconda OS-X」(OS-X用Anaconda)を検索すると以下のようなダウンロードのページの候補が表示されます。

Diving/GoogleAnaconda.png
Anacondaインストールのページに行くと、まず、Get Superpowers with Anaconda (Anacondaですごい力を得よう)と表示されますが、私には無意味なので無視します。OS-Xのリンクがありますので、そちらをクリックするとインストーラのダウンロードができます。

Diving/AnacondaInstall.png
とりあえずここではPython 2.7ではなくPython 3.5のコマンドラインのダウンロードをします。ダウンロードのリンクの下に始める際の説明が次のように記述されています。

コマンドラインインストーラ

1.コマンドラインインストーラをダウンロードしてください。
2.ターミナルのウィンドウに次のどちらかを入力し、指示に従ってください。Python 2.7: bash Anaconda2-2.5.0-MacOSX-x86_64.sh Python 3.5: bash Anaconda3-2.5.0-MacOSX-x86_64.sh
注:bashシェルを使っていなくても「bash」コマンドは必ず含むようにしてください。
3.オプション:MD5を使ってデータの整合性を確認してください。

Anacondaの容量は大きい(200メガバイト以上です)ので、ダウンロードに3、4分かかります。指示どおり、インストーラにコマンドラインを入力すると、数分かかるダウンロードとインストールが実行されます。

$ bash ../Downloads/Anaconda3-2.5.0-MacOSX-x86_64.sh

Welcome to Anaconda3 2.5.0 (by Continuum Analytics, Inc.)

In order to continue the installation process, please review the license agreement.
Please, press ENTER to continue
>>>
================
Anaconda License
================
...

そして、複数の質問に対しyesENTERを入力していくと次のように表示され終了します。

...
Prepending PATH=/Users/haoyi/anaconda3/bin to PATH in
newly created /Users/haoyi/.bash_profile

For this change to become active, you have to open a new terminal.

Thank you for installing Anaconda3!

Share your notebooks and packages on Anaconda Cloud!
Sign up for free: https://anaconda.org

ただちにanacondaを実行できるわけではなく、python3コマンドは変更されていないようです。

haoyi-mbp:test haoyi$ anaconda
-bash: anaconda: command ```not found

haoyi-mbp:test haoyi$ python3
Python 3.4.2 (default, Feb 10 2015, 03:38:22)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

インストールの出力で新しいターミナルを開かないと使えないと記述されているので、上は恐らく想定されていることです。新たにターミナルを開いてanacondaを実行すると次のように表示されます。

haoyi-mbp:~ haoyi$ anaconda
usage: anaconda [-h] [--show-traceback] [--hide-traceback] [-v] [-q] [--color]
                [--no-color] [-V] [-t TOKEN] [-s SITE]
                ...
anaconda: error: A sub command must be given. To show all available sub commands, run:

     anaconda -h

はっきり何がとは分かりませんが、インストールができました。python3を実行すると、次のように表示されます。

$ python3
Python 3.5.1 |Anaconda 2.5.0 (x86_64)| (default, Dec  7 2015, 11:24:55)
[GCC 4.2.1 (Apple Inc. build 5577)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

紹介バナーにAnacondaがあるので、Anacondaのインストールも無事成功したようです。指示の最後の点である「MD5を使ってデータの整合性の確認」については、YOLO(人生一度きり)なので、リスクは承知で思い切ってここでは置いておきます。

Spyderに戻る

Anacondaが整ったので、ソースコードで実行の部分に戻りましょう。ヘッダーの下の依存性について書かれている部分の前に次のような記述があります。

Spyderを最短で実行する方法は、gitを使ってソースコードを入手し、PyQt5またはPyQt4をインストールして次のコマンドを実行することです。

  1. 実行時の依存性をインストール(下記参照)
  2. cd /your/spyder/git-clone
  3. python bootstrap.py

これは、Spyderのバグ修正や新機能追加、使用方法の習得、または単にSpyderをいじってみることにした場合などに使うといいでしょう。

恐らくここではSpyderのリボジトリのクローンを作り、cd(ディレクトリの移動)するよう要求されています。では、Githubページに列挙されたURLを使ってやってみましょう。

Diving/SpyderCloneUrl.png

$ git clone git@github.com:spyder-ide/spyder.git
Cloning into 'spyder'...
remote: Counting objects: 43311, done.
remote: Compressing objects: 100% (393/393), done.
remote: Total 43311 (delta 263), reused 0 (delta 0), pack-reused 42918
Receiving objects: 100% (43311/43311), 28.27 MiB | 3.53 MiB/s, done.
Resolving deltas: 100% (33096/33096), done.
Checking connectivity... done.

もしこの記事の内容を追随したいのであれば、記事を書いている現段階のコミットである以下をお使いください。

そして、 cd(ディレクトリを移動)して実行します。

$ python3 bootstrap.py
Executing Spyder from source checkout
Revision df9577c, Branch: master
01. Patched sys.path with /Users/haoyi/test/spyder
02. No PyQt5 or PyQt4 detected, using PySide if available (deprecated)
Traceback (most recent call last):
  File "/Users/haoyi/test/spyder/spyderlib/qt/__init__.py", line 62, in <module>
    from PySide import __version__  # analysis:ignore
ImportError: No module named 'PySide'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "bootstrap.py", line 140, in <module>
    versions = get_versions(reporev=False)
  File "/Users/haoyi/test/spyder/spyderlib/__init__.py", line 65, in get_versions
    import spyderlib.qt
  File "/Users/haoyi/test/spyder/spyderlib/qt/__init__.py", line 65, in <module>
    raise ImportError("Spyder requires PyQt5, PyQt4 or PySide (deprecated) "
ImportError: Spyder requires PyQt5, PyQt4 or PySide (deprecated) to be installed
$

「下に列挙されている依存性全部ではありませんが、ほとんどはAnacondaが付いてくる…」と記載されていましたが、記載どおり、全部ではなく、ほとんどでした。これはいいとして、pip3を使ってPyQt5をインストールします(これは、githubのリードミーで「お勧め」されています)。pip3はPython 3用の python3実行ファイル対応のpipで、Anacondaに付いてきます。

PyQt5からPySideへ

$ pip3 install PyQt5
You are using pip version 6.0.8, however version 8.1.0 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Collecting PyQt5
  Could not find any downloads that satisfy the requirement PyQt5
  No distributions at all found for PyQt5
$

ダメですね。「pip install PyQt5」で検索すると次が表示されます。

一瞬pipを使ってもこのライブラリをPyPIからインストールできないように思えますが、さらに「osx pyqt5」を検索すると次が表示されます。

tarコマンドやmakeをコマンドラインに手動でひたすら入力する以外に簡単にPyQt5をインストールする方法がないかのように思えます。その一方で、上のコードでPySideモジュールが廃止になったとのエラーメッセージが表示されていましたが、大丈夫かもしれません。今は廃止警告を気にせず、とにかく何か(何でも)動けばいいと思っています。「pip install pyside」を検索すると、このモジュールの標準モジュールリポジトリのページを探索できます。

初めはpip3を使ってインストールができるようですが、しばらくすると次のように失敗に終わってしまいます。

$ pip3 install PySide
You are using pip version 6.0.8, however version 8.1.0 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Collecting PySide
  Downloading PySide-1.2.4.tar.gz (9.3MB)
    100% |################################| 9.3MB 69kB/s
    Removing /private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-8_5xp5w9/PySide/pyside_package
    package init file 'pyside_package/PySide/__init__.py' not found (or not a regular file)
    package init file 'pyside_package/pysideuic/__init__.py' not found (or not a regular file)
Installing collected packages: PySide
  Running setup.py install for PySide
    Removing /private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-8_5xp5w9/PySide/pyside_package
    Python architecture is 64bit
    error: Failed to find cmake. Please specify the path to cmake with --cmake parameter.
    Complete output from command /usr/local/opt/python3/bin/python3.4 -c "import setuptools, tokenize;__file__='/private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-8_5xp5w9/PySide/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-ugoc2jah-record/install-record.txt --single-version-externally-managed --compile:
    Removing /private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-8_5xp5w9/PySide/pyside_package

    running install

    running build

    Python architecture is 64bit

    error: Failed to find cmake. Please specify the path to cmake with --cmake parameter.

    ----------------------------------------
    Command "/usr/local/opt/python3/bin/python3.4 -c "import setuptools, tokenize;__file__='/private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-8_5xp5w9/PySide/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-ugoc2jah-record/install-record.txt --single-version-externally-managed --compile" failed with error code 1 in /private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-8_5xp5w9/PySide

cmakeを実行しようとすると存在しないと表示されてしまいます。 

$ cmake
-bash: cmake: command not found

多分パッケージマネージャ(brew)を使って単純にcmakeをインストールすれば問題ないのかもしれません。では、brew install cmakeで試してみましょう。

$ brew install cmake
==> Downloading http://www.cmake.org/files/v3.1/cmake-3.1.2.tar.gz
######################################################################## 100.0%
==> Downloading https://pypi.python.org/packages/source/S/Sphinx/Sphinx-1.2.3.tar.gz
######################################################################## 100.0%
==> python -c import setuptools... --no-user-cfg install --prefix=/private/tmp/cmake-Kp5igT/cmake-3.1
==> Downloading https://pypi.python.org/packages/source/d/docutils/docutils-0.12.tar.gz
######################################################################## 100.0%
==> python -c import setuptools... --no-user-cfg install --prefix=/private/tmp/cmake-Kp5igT/cmake-3.1
==> Downloading https://pypi.python.org/packages/source/P/Pygments/Pygments-2.0.2.tar.gz
######################################################################## 100.0%
==> python -c import setuptools... --no-user-cfg install --prefix=/private/tmp/cmake-Kp5igT/cmake-3.1
==> Downloading https://pypi.python.org/packages/source/J/Jinja2/Jinja2-2.7.3.tar.gz
######################################################################## 100.0%
==> python -c import setuptools... --no-user-cfg install --prefix=/private/tmp/cmake-Kp5igT/cmake-3.1
==> Downloading https://pypi.python.org/packages/source/M/MarkupSafe/MarkupSafe-0.23.tar.gz
######################################################################## 100.0%
==> python -c import setuptools... --no-user-cfg install --prefix=/private/tmp/cmake-Kp5igT/cmake-3.1
==> ./bootstrap --prefix=/usr/local/Cellar/cmake/3.1.2 --system-libs --parallel=8 --no-system-libarch
==> make
==> make install
/usr/local/Cellar/cmake/3.1.2: 1821 files, 29M, built in 5.1 minutes

苦痛の5分間が過ぎてやっと実行できたようです。cmakeコマンドで以下の内容が出力できるようになりました。

$ cmake
Usage

  cmake [options] <path-to-source>
  cmake [options] <path-to-existing-build>

Specify a source directory to (re-)generate a build system for it in the
current working directory.  Specify an existing build directory to
re-generate its build system.

Run 'cmake --help' for more information.

では再度 pip3 install PySideを試してみましょう。

$ pip3 install PySide
You are using pip version 6.0.8, however version 8.1.0 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Collecting PySide
  Using cached PySide-1.2.4.tar.gz
    Removing /private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-dx05oahd/PySide/pyside_package
    package init file 'pyside_package/PySide/__init__.py' not found (or not a regular file)
    package init file 'pyside_package/pysideuic/__init__.py' not found (or not a regular file)
Installing collected packages: PySide
  Running setup.py install for PySide
    Removing /private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-dx05oahd/PySide/pyside_package
    Python architecture is 64bit
    error: Failed to find qmake. Please specify the path to qmake with --qmake parameter.
    Complete output from command /usr/local/opt/python3/bin/python3.4 -c "import setuptools, tokenize;__file__='/private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-dx05oahd/PySide/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-ln1fsrej-record/install-record.txt --single-version-externally-managed --compile:
    Removing /private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-dx05oahd/PySide/pyside_package

    running install

    running build

    Python architecture is 64bit

    error: Failed to find qmake. Please specify the path to qmake with --qmake parameter.

    ----------------------------------------
    Command "/usr/local/opt/python3/bin/python3.4 -c "import setuptools, tokenize;__file__='/private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-dx05oahd/PySide/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-ln1fsrej-record/install-record.txt --single-version-externally-managed --compile" failed with error code 1 in /private/var/folders/xw/4lw_p32d0pg88hyg5rfn7vqxqhkgj_/T/pip-build-dx05oahd/PySide

このqmakeとは一体なのでしょうか。brew installは使えません。

$ brew install qmake
Error: No available formula for qmake
Searching formulae...
Searching taps...

pyside qmake osxを検索すると次が表示されます。

ここでは、Xcodeをインストールするように言っていまが、すでにインストール済みです。その後にマジックコマンドの入力があります。

brew install python qt

cmakeはインストール済みですし、python もAnacondaのセクションでインストール済みです。そのため、ここで必要になるのは、qtqmakeコマンドを使えるようにすることです。brew install qtを入力します。

$ brew install qt
==> Downloading https://download.qt.io/official_releases/qt/4.8/4.8.6/qt-everywhere-opensource-src-4.

curl: (22) The requested URL returned error: 403 Forbidden
Trying a mirror...
==> Downloading http://qtmirror.ics.com/pub/qtproject/official_releases/qt/4.8/4.8.6/qt-everywhere-op
######################################################################## 100.0%
==> Downloading https://raw.githubusercontent.com/DomT4/scripts/440e3cafde5bf6ec6f50cd28fa5bf89c280f1
######################################################################## 100.0%
==> Patching
patching file src/gui/dialogs/qcolordialog_mac.mm
patching file src/gui/dialogs/qfiledialog_mac.mm
patching file src/gui/dialogs/qfontdialog_mac.mm
patching file src/gui/kernel/qapplication_mac.mm
patching file src/gui/kernel/qcocoaapplication_mac.mm
patching file src/gui/kernel/qcocoaapplicationdelegate_mac.mm
Hunk #4 succeeded at 295 (offset -13 lines).
Hunk #5 succeeded at 342 (offset -13 lines).
patching file src/gui/kernel/qcocoaapplicationdelegate_mac_p.h
patching file src/gui/kernel/qcocoamenuloader_mac.mm
patching file src/gui/kernel/qcocoasharedwindowmethods_mac_p.h
patching file src/gui/kernel/qeventdispatcher_mac.mm
patching file src/gui/kernel/qt_cocoa_helpers_mac.mm
patching file src/gui/kernel/qwidget_mac.mm
patching file src/gui/styles/qmacstyle_mac.mm
patching file src/gui/util/qsystemtrayicon_mac.mm
patching file src/gui/widgets/qcocoamenu_mac.mm
patching file src/gui/widgets/qmenu_mac.mm
==> ./configure -prefix /usr/local/Cellar/qt/4.8.6 -system-zlib -qt-libtiff -qt-libpng -qt-libjpeg -c
==> make
2 errors generated.
make[2]: *** [.obj/release-shared/qpaintengine_mac.o] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [release] Error 2
make: *** [sub-gui-make_default-ordered] Error 2

READ THIS: http://git.io/brew-troubleshooting

タイムアウトか何かでインストールができなかったようです。この時点ではまだqmakeを実行できません。brew install qt timing outを検索すると次が出てきます。

https://github.com/Homebrew/homebrew/issues/30843

つまり、私だけが直面する問題ではないようです。もしかしたら、再実行すれば大丈夫なのかもしれません。もしできない場合はPyQt5を先ほどと同じように、指示通りに再インストールしてみましょう。

今回もまたダメだったようです。

==> ./configure -prefix /usr/local/Cellar/qt/4.8.6 -system-zlib -qt-libtiff -qt-libpng -qt-libjpeg -c
==> make
2 errors generated.
make[2]: *** [.obj/release-shared/qpaintengine_mac.o] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [release] Error 2
make: *** [sub-gui-make_default-ordered] Error 2

READ THIS: http://git.io/brew-troubleshooting

PyQt5に戻る

今はPySideのことは諦めましょう。理由は分かりませんが、インストールがうまくいきません。どちらにしろ、qtを要求しているようですね。おそらくPyQt4が要求しているものと大差ないでしょう。

今回もネットで「osx PyQt5」を検索してみると、こんなものがありました。

このパッケージはMac OS用のPyQt5があらかじめビルドされているバージョンをインストールするものです。既にコンパイルされたものを使用するため、これ自体は何もコンパイルしません。

これでうまくいくのでしょうか? コンパイルを避けるということは、brew install qtをする間にmakeコマンドを走らせている時、コンパイルのタイムアウトらしきものを避けるということに違いありません。思うに、コンパイルを必要とするPyQt5をインストールしようとするメカニズムはどれもqtが必要で、結局同じタイムアウトの問題にぶつかるのではないでしょうか。どちらにしても、これはうまくインストールできたようです。

$ pip3 install pyqt5-macos-built
You are using pip version 6.0.8, however version 8.1.0 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Collecting pyqt5-macos-built
  Downloading pyqt5-macos-built-5.5.0.tar.gz (5.1MB)
    100% |################################| 5.1MB 124kB/s
    /usr/local/Cellar/python3/3.4.2_1/Frameworks/Python.framework/Versions/3.4/lib/python3.4/distutils/dist.py:260: UserWarning: Unknown distribution option: 'bugtrack_url'
      warnings.warn(msg)
Collecting docopt (from pyqt5-macos-built)
  Downloading docopt-0.6.2.tar.gz
Installing collected packages: docopt, pyqt5-macos-built
  Running setup.py install for docopt
  Running setup.py install for pyqt5-macos-built
    changing mode of build/scripts-3.4/pyqt5_macos_built.py from 644 to 755
    /usr/local/Cellar/python3/3.4.2_1/Frameworks/Python.framework/Versions/3.4/lib/python3.4/distutils/dist.py:260: UserWarning: Unknown distribution option: 'bugtrack_url'
      warnings.warn(msg
    changing mode of /usr/local/bin/pyqt5_macos_built.py to 755
Successfully installed docopt-0.6.2 pyqt5-macos-built-5.5.0

奇妙なことに、python3 bootstrap.pyを実行しようとすると、PyQt5が見つからないといって処理に失敗するようです。python3のREPLではうまくインポートできるのに、おかしいですね。

$ python3 bootstrap.py
Executing Spyder from source checkout
Revision df9577c, Branch: master
01. Patched sys.path with /Users/haoyi/test/spyder
02. PyQt5 is detected, selecting
Traceback (most recent call last):
  File "/Users/haoyi/test/spyder/spyderlib/qt/__init__.py", line 62, in <module>
    from PySide import __version__  # analysis:ignore
ImportError: No module named 'PySide'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "bootstrap.py", line 140, in <module>
    versions = get_versions(reporev=False)
  File "/Users/haoyi/test/spyder/spyderlib/__init__.py", line 65, in get_versions
    import spyderlib.qt
  File "/Users/haoyi/test/spyder/spyderlib/qt/__init__.py", line 65, in <module>
    raise ImportError("Spyder requires PyQt5, PyQt4 or PySide (deprecated) "
ImportError: Spyder requires PyQt5, PyQt4 or PySide (deprecated) to be installed
$ python3
Python 3.4.2 (default, Feb 10 2015, 03:38:22)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import PyQt5
>>>

 spyderlib/qt/__init__.py内のインポートを実行するコードを見てみると、以下のようになっています。

if API == 'pyqt5':
    try:
        from PyQt5.QtCore import PYQT_VERSION_STR as __version__
        from PyQt5 import uic  # analysis:ignore
    except ImportError:
        API = os.environ['QT_API'] = 'pyqt'
        API_NAME = 'PyQt4'

同じファイルの下方で失敗に終わるImportErrorにぶち当たりそうな気がします。試しにpython3のREPLでこのインポートを実行してみることも可能です。そして、驚いたことに失敗です。

$ python3
Python 3.4.2 (default, Feb 10 2015, 03:38:22)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from PyQt5.QtCore import PYQT_VERSION_STR as __version__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: dlopen(/usr/local/lib/python3.4/site-packages/PyQt5/QtCore.so, 2): Library not loaded: /usr/local/opt/qt5/lib/QtCore.framework/Versions/5/QtCore
  Referenced from: /usr/local/lib/python3.4/site-packages/PyQt5/QtCore.so
  Reason: image not found
>>>

ついていませんね。import PyQt5はできそうに思えましたが、from PyQt5.Core import PYQT_VERSION_STR as __version__を行うのは無理そうです。一部のバイナリ依存性がきちんとインストールされていないのでしょうか。もしかしたら、pyqt5-macos-builtのパッケージには、本来必要な何かしらのQT依存性が欠けているのかもしれません。

ネットで「Library not loaded QtCore (QtCoreライブラリがロードされていない)」を検索してみたら、これを見つけました。

もしかして、brew install qtがうまく動作しなかったらbrew install qt5が動作するのでしょうか?

これは時間がかかりすぎる気がします。condapipからPythonのパッケージをインストールできることが判明しました。ネットで「Anaconda pyqt5」と検索すると下記が出てきました。

conda config --add channels dsdale24 
conda install pyqt5

python3が何らかの事情でpyqt5の何かしらと互換性がないため、これはうまくいかないみたいです。pyqt5向けのAnacondaのパッケージはPython 2.7でしか使えないことが分かりました。

$ conda info pyqt5
Using Anaconda Cloud api site https://api.anaconda.org
Fetching package metadata: ......

pyqt5 5.3.1 py27_0
------------------
...

幸いcondaには便利なコマンドがたくさんあり、中でもsearchコマンドは、使える可能性のある似たようなものを探すのに一役買ってくれます。

$ conda
usage: conda [-h] [-V] [--debug] command ...

conda is a tool for managing and deploying applications, environments and packages.

Options:

positional arguments:
  command
    info         Display information about current conda install.
    help         Displays a list of available conda commands and their help
                 strings.
    list         List linked packages in a conda environment.
    search       Search for packages and display their information. The input
                 is a Python regular expression. To perform a search with a
                 search string that starts with a -, separate the search from
                 the options with --, like 'conda search -- -h'. A * in the
                 results means that package is installed in the current
                 environment. A . means that package is not installed but is
                 cached in the pkgs directory.

これでpyqtのPython 3で動作するバージョンを見つけられるでしょうか?

$ conda search pyqt
Using Anaconda Cloud api site https://api.anaconda.org
Fetching package metadata: ......
pyqt                         4.10.4                   py26_0  defaults
                             4.10.4                   py27_0  defaults
                             4.10.4                   py33_0  defaults
                             4.10.4                   py34_0  defaults
                             4.11.3                   py26_0  defaults
                             4.11.3                   py27_0  defaults
                             4.11.3                   py33_0  defaults
                             4.11.3                   py34_0  defaults
                             4.11.3                   py35_0  defaults
                             4.11.4                   py27_0  defaults
                             4.11.4                   py34_0  defaults
                             4.11.4                   py35_0  defaults
                             4.11.4                   py27_1  defaults
                             4.11.4                   py34_1  defaults
                          *  4.11.4                   py35_1  defaults
pyqt5                        5.3.1                    py27_0  dsdale24
pyqtgraph                    0.9.10                   py27_0  defaults
                             0.9.10                   py34_0  defaults
                             0.9.10                   py35_0  defaults
                             0.9.10                   py27_1  defaults
                             0.9.10                   py34_1  defaults
                             0.9.10                   py35_1  defaults

pyqtにはpy35_0(私たちのです)向けのパッケージがあるようです。pyqt5にはないですけれどね。代わりにこれをダウンロードしてみましょうか。

pyqt(バージョン4)を使うとうまくいきそうです。

$ conda install pyqt
Using Anaconda Cloud api site https://api.anaconda.org
Fetching package metadata: ......
Solving package specifications: ..........................
Package plan for installation in environment /Users/haoyi/anaconda3:

The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    openssl-1.0.2g             |                0         3.0 MB
    xz-5.0.5                   |                1         173 KB
    setuptools-20.2.2          |           py35_0         458 KB
    wheel-0.29.0               |           py35_0          82 KB
    conda-4.0.4                |           py35_0         188 KB
    pip-8.1.0                  |           py35_0         1.6 MB
    ------------------------------------------------------------
                                           Total:         5.4 MB

The following packages will be UPDATED:

    conda:      3.19.1-py35_0 --> 4.0.4-py35_0
    openssl:    1.0.2f-0      --> 1.0.2g-0
    pip:        8.0.2-py35_0  --> 8.1.0-py35_0
    setuptools: 19.6.2-py35_0 --> 20.2.2-py35_0
    wheel:      0.26.0-py35_1 --> 0.29.0-py35_0
    xz:         5.0.5-0       --> 5.0.5-1

Proceed ([y]/n)? y

Fetching packages ...
openssl-1.0.2g 100% |#####################################################| Time: 0:00:08 393.54 kB/s
xz-5.0.5-1.tar 100% |#####################################################| Time: 0:00:00 184.22 kB/s
setuptools-20. 100% |#####################################################| Time: 0:00:01 238.96 kB/s
wheel-0.29.0-p 100% |#####################################################| Time: 0:00:00 102.15 kB/s
conda-4.0.4-py 100% |#####################################################| Time: 0:00:00 199.95 kB/s
pip-8.1.0-py35 100% |#####################################################| Time: 0:00:02 673.73 kB/s
Extracting packages ...
[      COMPLETE      ]|########################################################################| 100%
Unlinking packages ...
[      COMPLETE      ]|########################################################################| 100%
Linking packages ...
[      COMPLETE      ]|########################################################################| 100%
$ 

これでbootstrapのスクリプトを実行した時にクラッシュすることはなさそうです。

$ python3 bootstrap.py
Executing Spyder from source checkout
Revision df9577c, Branch: master
01. Patched sys.path with /Users/haoyi/test/spyder
02. PyQt4 is detected, selecting
03. Imported Spyder 3.0.0dev
    [Python 3.5.1 64bits, Qt 4.8.7, PyQt4 (API v2) 4.11.4 on Darwin]
04. Running Spyder

いや、そうでもないかもしれません。

Bootstrap completed in 00:00:13.3716
Traceback (most recent call last):
  File "bootstrap.py", line 162, in <module>
    start.main()
  File "/Users/haoyi/test/spyder/spyderlib/app/start.py", line 118, in main
    from spyderlib.app import spyder
  File "/Users/haoyi/test/spyder/spyderlib/app/spyder.py", line 99, in <module>
    from spyderlib.utils.qthelpers import qapplication
  File "/Users/haoyi/test/spyder/spyderlib/utils/qthelpers.py", line 16, in <module>
    import spyderlib.utils.icon_manager as ima
  File "/Users/haoyi/test/spyder/spyderlib/utils/icon_manager.py", line 14, in <module>
    import qtawesome as qta
ImportErro
r: No module named 'qtawesome'

これは一体何でしょう? conda installができるようには思えません。しかし、ビルトインの検索機能を使ったところ、期待の持てそうなものが出てきました。

$ conda install qtawesome
Using Anaconda Cloud api site https://api.anaconda.org
Fetching package metadata: ......
Solving package specifications: .
Error:  Package missing in current osx-64 channels:
  - qtawesome

You can search for this package on anaconda.org with

    anaconda search -t conda qtawesome
$ anaconda search -t conda qtawesome
Using Anaconda Cloud api site https://api.anaconda.org
Run 'anaconda show <USER/PACKAGE>' to get more details:
Packages:
     Name                      |  Version | Package Types   | Platforms
     ------------------------- |   ------ | --------------- | ---------------
     lightmotif/qtawesome      |   0.1.10 | conda           | win-32
                                          : FontAwesome icons in PyQt and PySide applications
     spyder-ide/qtawesome      |    0.2.0 | conda           | None-None
                                          : Iconic fonts in PyQt and PySide applications
Found 2 packages

これをどうやってインストールすればいいのでしょう?

ネットでqtawesomeを検索したら、こういうものが見つかりました。

Qt向けのFontAwesomeみたいなもののようですね。きっとこれは矢印などの見た目を良くするために作られたのだと思います。

pip3を使ったインストールはうまくいきそうです。pip3でのインストールは、conda installを使うのといくらか違うのでしょうか。とにかく、pip3は成功しました。

$ pip3 install QtAwesome
You are using pip version 6.0.8, however version 8.1.0 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Collecting QtAwesome
  Downloading QtAwesome-0.3.1-py2.py3-none-any.whl (149kB)
    100% |################################| 151kB 2.7MB/s
Collecting qtpy (from QtAwesome)
  Downloading QtPy-0.1.3-py2.py3-none-any.whl
Collecting six (from QtAwesome)
  Using cached six-1.10.0-py2.py3-none-any.whl
Installing collected packages: six, qtpy, QtAwesome



Successfully installed QtAwesome-0.3.1 qtpy-0.1.3 six-1.10.0

しかし、インポートしようとすると実際には動きません。

$ python3
Python 3.5.1 |Anaconda 2.5.0 (x86_64)| (default, Dec  7 2015, 11:24:55)
[GCC 4.2.1 (Apple Inc. build 5577)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import qtawesome
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'qtawesome'
>>>

ネットでconda install "qtawesome"を検索したら(単語が切り離されてしまわないようクオーテーションが必須です)、こちらが見つかりました。
* https://anaconda.org/lightmotif/qtawesome

これはうまくいきません。

$ conda install -c https://conda.anaconda.org/lightmotif qtawesome
Using Anaconda Cloud api site https://api.anaconda.org
Fetching package metadata: ........
Solving package specifications: .
Error:  Package missing in current osx-64 channels:
  - qtawesome

You can search for this package on anaconda.org with

    anaconda search -t conda qtawesome

しかし、anaconda searchで先に列挙されていた”他の”foo/qtawesomeの名前を使うことで、機能します。

$ conda install -c https://conda.anaconda.org/spyder-ide qtawesome
Using Anaconda Cloud api site https://api.anaconda.org
Fetching package metadata: ........
Solving package specifications: ..........

Package plan for installation in environment /Users/haoyi/anaconda3:

The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    qtpy-1.0b1                 |             py_0          10 KB
    qtawesome-0.2.0            |             py_0         146 KB
    ------------------------------------------------------------
                                           Total:         156 KB

The following NEW packages will be INSTALLED:

    qtawesome: 0.2.0-py_0
    qtpy:      1.0b1-py_0

Proceed ([y]/n)? y

Fetching packages ...
qtpy-1.0b1-py_ 100% |#####################################################| Time: 0:00:00  38.93 kB/s
qtawesome-0.2. 100% |#####################################################| Time: 0:00:01 106.19 kB/s
Extracting packages ...
[      COMPLETE      ]|########################################################################| 100%
Linking packages ...
[      COMPLETE      ]|########################################################################| 100%

こうしてpython3 bootstrap.pyを終えて、やっと起動することができました。

Diving/SpyderSplash.png

いや、できていないかもしれません。

Bootstrap completed in 00:00:01.8318
Traceback (most recent call last):
  File "/Users/haoyi/test/spyder/spyderlib/app/spyder.py", line 3130, in main
    mainwindow = run_spyder(app, options, args)
  File "/Users/haoyi/test/spyder/spyderlib/app/spyder.py", line 3026, in run_spyder
    main.post_visible_setup()
  File "/Users/haoyi/test/spyder/spyderlib/app/spyder.py", line 1316, in post_visible_setup
    self.report_missing_dependencies()
  File "/Users/haoyi/test/spyder/spyderlib/app/spyder.py", line 1322, in report_missing_dependencies
    missing_deps = dependencies.missing_dependencies()
  File "/Users/haoyi/test/spyder/spyderlib/dependencies.py", line 108, in missing_dependencies
    if not dependency.check() and not dependency.optional:
  File "/Users/haoyi/test/spyder/spyderlib/dependencies.py", line 46, in check
    self.installed_version)
  File "/Users/haoyi/test/spyder/spyderlib/utils/programs.py", line 429, in is_module_installed
    actver = get_module_version(module_name)
  File "/Users/haoyi/test/spyder/spyderlib/utils/programs.py", line 371, in get_module_version
    mod = __import__(module_name)
  File "/Users/haoyi/anaconda3/lib/python3.5/site-packages/jedi/__init__.py", line 41, in <module>
    from jedi.api import Script, Interpreter, NotFoundError, set_debug_function
  File "/Users/haoyi/anaconda3/lib/python3.5/site-packages/jedi/api/__init__.py", line 16, in <module>
    from jedi.parser import Parser, load_grammar
  File "/Users/haoyi/anaconda3/lib/python3.5/site-packages/jedi/parser/__init__.py", line 21, in <module>
    from jedi.parser import tree as pt
  File "/Users/haoyi/anaconda3/lib/python3.5/site-packages/jedi/parser/tree.py", line 39, in <module>
    from jedi import cache
  File "/Users/haoyi/anaconda3/lib/python3.5/site-packages/jedi/cache.py", line 32, in <module>
    from jedi import debug
  File "/Users/haoyi/anaconda3/lib/python3.5/site-packages/jedi/debug.py", line 17, in <module>
    init()
  File "/Users/haoyi/anaconda3/lib/python3.5/site-packages/colorama/initialise.py", line 37, in init
    wrap_stream(orig_stdout, convert, strip, autoreset, wrap)
  File "/Users/haoyi/anaconda3/lib/python3.5/site-packages/colorama/initialise.py", line 76, in wrap_stream
    convert=convert, strip=strip, autoreset=autoreset)
  File "/Users/haoyi/anaconda3/lib/python3.5/site-packages/colorama/ansitowin32.py", line 67, in __init__
    strip = conversion_supported or (not wrapped.closed and not is_a_tty(wrapped))
AttributeError: 'SysOutput' object has no attribute 'closed'

ネットでこのエラーメッセージを検索したら、次のものが見つかりました。

解決策は以下のどちらかのようです。

  • pip install colorama==0.3.5
  • conda install colorama=0.3.3

この問題について投稿した人たちのどちらを信じるかによりますね。1つ目の方はうまくいきそうもありませんが、2つ目はうまくいきそうです。Spyderが立ち上がります。

こんなものが出てしまいました。

Diving/SpyderDependencies.png
しかしこれは単にconda install pylintをすれば、機能するようになります。

Diving/SpyderWorks.png
これをある程度長い時間(2~3分?)そのまま走らせておいたら、下記のようになって終わることに気付きました。

Segmentation fault: 11

ですから完全に安定しているわけではないのです。しかし私たちが変更の実行とテストを行う分には十分です。ということで、ここではよしとしましょう。


私がSpyderの「Scientific Python」IDEへの変更を最初に試み始めてから、ここまでで約2時間が経ちました。ついに「開発」モードでソースから立ち上げることができたのです。外部のコードベースを理解しようとする時、これは最も重要なステップです。ここまで私たちは、依存性がうまく機能しない理由を見つけ出そうとして1、2回コードをちらっと見ただけです。次のステップでは、コードの何がどうなっているのかを探るため、コードを詳しく見ていきましょう。


この記事の後編はこちら:他人の書いたコードに挑もう – Part 2

article originally written by Li Haoyi on http://www.lihaoyi.com/post/DivingIntoOtherPeoplesCode.html