非ASCIIを間違った場所にプッシュする

彼は夕方に家に座って、何をすべきか考えました。 あ! Pythonにはデバッガーがありますが、入力のための完全にpromptいプロンプトがあります。 そこで電力線を切ってみましょう。 ポイントは完全に些細なことのように思えます:独自のプロパティで独自のサブクラスpdb.Pdbを作成する必要がありますよね?
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に変換されます)、プロンプト文字列のさまざまな操作を使用してこれを行います。



したがって、必要なものは次のとおりです。
  1. 非ASCII文字に対してエラーをスローせずにstr



    変換するunicode



    下位クラス(変換は単純にstr(prompt)



    形式で実行されstr(prompt)



    )。 この部分は非常に簡単です__new__



    __str__



    __new__



    __str__



    を再定義する必要があり__str__



    (原則として、2番目の__str__



    せずに実行できますが、次からこのクラスに変換する場合、使用するエンコードを明示的に指定できるため、より便利です)
  2. 前のクラスが変換されるstr



    下位クラス。 ここで2つのメソッドをオーバーライドすることは、カテゴリー的には十分ではありません。
    1. __new__



      、エンコーディングの便利な保存と、 unicode



      str



      への明示的な変換の必要性を__new__



      ために必要です。
    2. __contains__



      および他のいくつかのメソッドは、現在のクラスがunicode



      かのようにUnicode引数で動作するはずです(非unicode



      引数の場合、何も変更する必要はありません)。 事実は'\n' in prompt



      unicode_literals '\n' in prompt



      存在する場合、Pythonがprompt



      unicode



      にキャストしようとしているため、 prompt



      が非ASCII文字のバイト文字列である場合、例外をスローしprompt



    3. find



      および同様の関数は、現在のエンコーディングのバイト文字列であるかのように、Unicode引数で動作するはずです。 これは、正しいインデックスを作成するために必要ですが、バイト文字列をユニコードに変換することによるエラーで失敗しないようにするためです(そして、変換が反対ではない理由はここにあります)。
    4. __len__



      は、Unicodeコードポイントで文字列__len__



      長さを__len__



      必要があります。 この部分は、招待の終了位置を考慮して(それに応じてカーソルを置く)pyreplがミスを犯さず、招待とカーソルの間に巨大なスペースを作らないようにするために必要です。 実際にはコードポイントではなく、画面のセルの行幅を使用する必要があると思います(たとえば、Vimでstrdisplaywidth()が行うことです)。
    5. __add__



      は、Unicode文字列に追加されたときに最初のunicode



      下位クラスを返す必要があります。 __radd__



      も同じようにする必要があります。 バイト文字列を追加すると、 str



      クラスが子孫になります。 詳細については、次の段落で説明します。
    6. 最後に、 __getslice__



      (注意: __getitem__



      はロールしません__getslice__



      はスライスに非推奨の__getslice__



      を使用しstr



      は、最後に__getslice__



      空のUnicode文字列、現在のクラスからのスライス、それに別のスライスを追加するため、同じクラスのオブジェクトを返します。 そして、この部分を無視すると、再びUnicodeErrorが発生します。
その結果、次の2つの異常が発生します。
 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



と入力すると表示される内容: 電力線IPythonおよびリライトプロンプト (一番下の行)。



All Articles