def use_powerline_prompt(cls): '''Decorator that installs powerline prompt to the class ''' @property def prompt(self): try: powerline = self.powerline except AttributeError: powerline = PDBPowerline() powerline.setup(self) self.powerline = powerline return powerline.render(side='left') @prompt.setter def prompt(self, _): pass cls.prompt = prompt return cls
いや Python-3では、このようなコードは引き続き機能しますが、Python-2では問題が待ち受けています。出力するには、エンコードを必要とするUnicode文字列を一連のバイトに変換する必要があります。 まあ、それは簡単です: encoding = get_preferred_output_encoding() def prompt(self): … ret = powerline.render(side='left') if not isinstance(ret, str): # Python-2 ret = ret.encode(encoding) return ret
。 それは簡単で、ユーザーがpdbppをインストールするまで機能します。 ここで、pdbppがpyreplを使用でき、pyreplがUnicodeで動作しないという事実に関連するいくつかのエラーが表示されます(さらに、pyreplが使用されるかどうかは、何らかの理由で$TERM
TERM¹に依存します)。 誰かが招待状にUnicodeを表示したくないという事実に関連するエラーは新しいものではありません。IPythonはUnicodeがプロンプトを書き換えることを防止しようとしました²。 しかし、ここではすべてがはるかに悪いです:pyreplは
from __future__ import unicode_literals
使用しますが
from __future__ import unicode_literals
通常の文字列(このインポートによってUnicodeに変換されます)、プロンプト文字列のさまざまな操作を使用してこれを行います。
したがって、必要なものは次のとおりです。
- 非ASCII文字に対してエラーをスローせずに
str
変換するunicode
下位クラス(変換は単純にstr(prompt)
形式で実行されstr(prompt)
)。 この部分は非常に簡単です__new__
__str__
と__new__
__str__
を再定義する必要があり__str__
(原則として、2番目の__str__
せずに実行できますが、次からこのクラスに変換する場合、使用するエンコードを明示的に指定できるため、より便利です) - 前のクラスが変換される
str
下位クラス。 ここで2つのメソッドをオーバーライドすることは、カテゴリー的には十分ではありません。-
__new__
、エンコーディングの便利な保存と、unicode
→str
への明示的な変換の必要性を__new__
ために必要です。 -
__contains__
および他のいくつかのメソッドは、現在のクラスがunicode
かのようにUnicode引数で動作するはずです(非unicode
引数の場合、何も変更する必要はありません)。 事実は'\n' in prompt
unicode_literals'\n' in prompt
存在する場合、Pythonがprompt
をunicode
にキャストしようとしているため、prompt
が非ASCII文字のバイト文字列である場合、例外をスローしprompt
。 -
find
および同様の関数は、現在のエンコーディングのバイト文字列であるかのように、Unicode引数で動作するはずです。 これは、正しいインデックスを作成するために必要ですが、バイト文字列をユニコードに変換することによるエラーで失敗しないようにするためです(そして、変換が反対ではない理由はここにあります)。 -
__len__
は、Unicodeコードポイントで文字列__len__
長さを__len__
必要があります。 この部分は、招待の終了位置を考慮して(それに応じてカーソルを置く)pyreplがミスを犯さず、招待とカーソルの間に巨大なスペースを作らないようにするために必要です。 実際にはコードポイントではなく、画面のセルの行幅を使用する必要があると思います(たとえば、Vimでstrdisplaywidth()が行うことです)。 -
__add__
は、Unicode文字列に追加されたときに最初のunicode
下位クラスを返す必要があります。__radd__
も同じようにする必要があります。 バイト文字列を追加すると、str
クラスが子孫になります。 詳細については、次の段落で説明します。 - 最後に、
__getslice__
(注意:__getitem__
はロールしません__getslice__
はスライスに非推奨の__getslice__
を使用しstr
は、最後に__getslice__
空のUnicode文字列、現在のクラスからのスライス、それに別のスライスを追加するため、同じクラスのオブジェクトを返します。 そして、この部分を無視すると、再びUnicodeErrorが発生します。
-
class PowerlineRenderBytesResult(bytes): def __new__(cls, s, encoding=None): encoding = encoding or s.encoding self = bytes.__new__(cls, s.encode(encoding) if isinstance(s, unicode) else s) self.encoding = encoding return self for meth in ( '__contains__', 'partition', 'rpartition', 'split', 'rsplit', 'count', 'join', ): exec(( 'def {0}(self, *args):\n' ' if any((isinstance(arg, unicode) for arg in args)):\n' ' return self.__unicode__().{0}(*args)\n' ' else:\n' ' return bytes.{0}(self, *args)' ).format(meth)) for meth in ( 'find', 'rfind', 'index', 'rindex', ): exec(( 'def {0}(self, *args):\n' ' if any((isinstance(arg, unicode) for arg in args)):\n' ' args = [arg.encode(self.encoding) if isinstance(arg, unicode) else arg for arg in args]\n' ' return bytes.{0}(self, *args)' ).format(meth)) def __len__(self): return len(self.decode(self.encoding)) def __getitem__(self, *args): return PowerlineRenderBytesResult(bytes.__getitem__(self, *args), encoding=self.encoding) def __getslice__(self, *args): return PowerlineRenderBytesResult(bytes.__getslice__(self, *args), encoding=self.encoding) @staticmethod def add(encoding, *args): if any((isinstance(arg, unicode) for arg in args)): return ''.join(( arg if isinstance(arg, unicode) else arg.decode(encoding) for arg in args )) else: return PowerlineRenderBytesResult(b''.join(args), encoding=encoding) def __add__(self, other): return self.add(self.encoding, self, other) def __radd__(self, other): return self.add(self.encoding, other, self) def __unicode__(self): return PowerlineRenderResult(self) class PowerlineRenderResult(unicode): def __new__(cls, s, encoding=None): encoding = ( encoding or getattr(s, 'encoding', None) or get_preferred_output_encoding() ) if isinstance(s, unicode): self = unicode.__new__(cls, s) else: self = unicode.__new__(cls, s, encoding, 'replace') self.encoding = encoding return self def __str__(self): return PowerlineRenderBytesResult(self)
(Python2では、 bytes is str
)。
これまでのgithubでの結果は私のブランチにのみあり、後でメインリポジトリの
develop
ます。
もちろん、結果はpyreplだけに限定されず、非ASCII文字列をスリップできないが実際にしたいさまざまな場所に適用できます。
TERM=xterm-256color
場合、pyreplからエラーが発生しますが、
TERM=
または
TERM=konsole-256color
-いいえ、すべて
TERM=konsole-256color
動作します。
²IPythonで自動呼び出しを有効にし、
int 42
と入力すると表示される内容: (一番下の行)。