このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

ソフトウェアのインストールや更新はやっぱりメンドクサイ

僕はソフトウェアのインストールとか更新とかはメンドクサイので嫌い。だけど、Mercurialを1.4.3から2.1にして少しハズミがついて、IPython(http://ipython.org/*1を入れる気になりました。http://ipython.org/download.html から ipython-0.12.win32-setup.exe をダウンロードしてインストール後に起動してみると、


hlen_b4_cell = self.readline.get_current_history_length()
AttributeError: 'module' object has no attribute 'get_current_history_length'

と言って起動失敗。


>>> dir(readline)

で調べてみると、get_history_length はあるけど、get_current_history_length はないですね。PyReadline(http://ipython.org/pyreadline.html)が古いらしい(pyreadline-1.5-win32 を使っていた)。https://launchpad.net/pyreadline/+download (最新版のソースは https://github.com/pyreadline/pyreadline)から pyreadline-1.7.1.win32.exe をダウンロードしてインストール。

これで、IPythonは動くようになりメデタシメデタシ。と思ったら、Catyが動かなくなってしまいました。ガーン。

最近のPyReadlineの使い方はどうも以前と変わったようです。PyReadlineはGNU readlineが使えない環境(主にWindows)用に準備されたものなので、以前は次のようにして使用していました。

if sys.platform == 'win32':
    import pyreadline as readline
else:
    import readline

しかし最近は、こういう場合分けは不要で、単に import readline とすればいいようです。import pyreadline とすると変なことになります。

僕の環境で言えば、import pyreadline だと Python26/Lib/site-packages/pyreadline/__init__.py(のコンパイル済みファイル)がロードされ、 import readline だと Python26/Lib/site-packages/readline.py がロードされます。以前は readline.py がなかったのでしょう(1.5に戻して確認する気はない)。

プラットフォームによる場合分けは必要なくなったのですが、readlineが存在しない環境はあり得るので、結局次のようなコードが必要です(Kuwataさんが http://return0.info/note/2011-12.html#id2011-12-17 で書いています)。

try:
    import readline
except:
    readline = None
    print '[Warning] readline module is not installed.'

# ... 

if readline is not None:
     # ...

このような修正をしてCatyは起動するようになったのですが、今度はWin32 APIを使ったクリアスクリーンが動かなくなっちゃいました。


Error cls: Col 0, Line 1
Traceback (most recent call last):
File "./python\caty\front\console.py", line 322, in default
r = c(None)
File "./python\caty\core\facility\__init__.py", line 308, in __call__
r = self._command(input)
File "./python\caty\core\script\interpreter.py", line 91, in __call__
return self.cmd.accept(self)
File "./python\caty\core\command\__init__.py", line 226, in accept
return visitor.visit_command(self)
File "./python\caty\core\script\interpreter.py", line 102, in visit_command
return self._exec_command(node, self._do_command)
File "./python\caty\core\script\interpreter.py", line 148, in _exec_command
r = exec_func(node)
File "./python\caty\core\script\interpreter.py", line 109, in _do_command
return args[0].execute()
File "/cls.py", line 9, in execute
File "./lib/win32cls.py", line 67, in clear_screen
coord, byref(dwDummy))
ArgumentError: argument 4: : wrong type

argument 4: : wrong type
caty:root>

今まで動いていた次のコードがエラーしてるんですよね。いかなる因果関係なのかサッパリわかりません。ムー。

# 画面クリア

def clear_screen ():
  csbi = CONSOLE_SCREEN_BUFFER_INFO()
  coord = COORD() # 初期値 coord.X = 0, coord.Y = 0
  dwDummy = DWORD()

  hConOut = stdout_handle
  if GetConsoleScreenBufferInfo (hConOut, byref(csbi)):
    FillConsoleOutputCharacterA (hConOut, 32, 
                                 csbi.dwSize.X * csbi.dwSize.Y, 
                                 coord, byref(dwDummy))
    FillConsoleOutputAttribute (hConOut, csbi.wAttributes, 
                                csbi.dwSize.X * csbi.dwSize.Y, 
                                coord, byref(dwDummy)) # ここが 67 行目
    SetConsoleCursorPosition (hConOut, coord)

動き出したIPythonでも不思議なことが起こります。Mercurialのライブラリも入れた(つうか結果的に入った)ので、IPythonからインポートしてみると:


In [7]: import mercurial.hg

^A^[[0;32m^BIn [^A^[[1;32m^B8^A^[[0;32m^B]: ^A^[[0m^B

^A^[[0;32m^BIn [^A^[[1;32m^B8^A^[[0;32m^B]: ^A^[[0m^B

文字に直して貼りつけてますが、プロンプトとしてコントロールコードが出ているようです。どうも、ANSIエスケープシーケンスを使うモードに変わっているようです。mercurial.hg のなかでコンソールに影響する設定をしてるのでしょうかね。

複雑になったソフトウェアシステムでは、予期せぬ相互作用で不具合や奇妙な現象が生じるわけですが、もうメンドクサくて追いかける気力が湧きませんなー。

[追記]

IPythonのコンソールが壊れる原因は、どうやら、mercurial/windows.py のようだ。余分な部分を削り落とすと次のようなコード。Windowsのstdoutの不具合を修復する目的らしいが、他の所ではこれが迷惑だったりする。

# windows.py - Windows utility function implementations for Mercurial
#
#  Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.


import sys

class winstdout(object):
    '''stdout on windows misbehaves if sent through a pipe'''

    def __init__(self, fp):
        self.fp = fp

    def __getattr__(self, key):
        return getattr(self.fp, key)

    def close(self):
        try:
            self.fp.close()
        except IOError:
            pass

    def write(self, s):
        try:
            # This is workaround for "Not enough space" error on
            # writing large size of data to console.
            limit = 16000
            l = len(s)
            start = 0
            self.softspace = 0
            while start < l:
                end = start + limit
                self.fp.write(s[start:end])
                start = end
        except IOError, inst:
            if inst.errno != 0:
                raise
            self.close()
            raise IOError(errno.EPIPE, 'Broken pipe')

    def flush(self):
        try:
            return self.fp.flush()
        except IOError, inst:
            if inst.errno != errno.EINVAL:
                raise
            self.close()
            raise IOError(errno.EPIPE, 'Broken pipe')

sys.__stdout__ = sys.stdout = winstdout(sys.stdout)

[/追記]

[追記]クリアスクリーンの問題は事情が分からないので、OSネイティブのclsコマンドを呼び出すことでとりあえず対処。[/追記]

*1:IPythonて、「高機能なPythonシェル」だと思っていたのですが、http://ipython.org/ipython-doc/rel-0.12/parallel/index.html とかを眺めると、なんか分散コンピューティングのプラットフォームを提供したりするんですね。スゴイ。