TIL

Today I Learned. 知ったこと、学んだことを書いていく

os.path.expanduser関数 - Python

カレントユーザのホームディレクトリのパスを取得することができる

>>> os.path.expanduser('~/src/python/')
'/Users/tamago324/src/python/'

使えるときあるのかな?

参考文献

【IFNULL関数】 AがNULLだったら、B。AがNULLじゃなかったら、Aを返す関数 - MySQL

便利なのを知ったからメモ

もし、第1パラメータがNULLだったら、第2パラメータを返し、
第1パラメータがNULLじゃなかったら、第2パラメータを返してくれる便利なやつ

第1パラメータがNULLではない時

SELECT IFNULL('AAA', 'BBB') FROM DUAL

結果

AAA

第1パラメータが返ってくる

第1パラメータがNULLの時

SELECT IFNULL(NULL, 'BBB') FROM DUAL

結果

BBB

第2パラメータが返ってくる

Pythonでのorみたいな?

>>> 'hoge' or 'fuga'
'hoge'

>>> None or 'fuga'
'fuga'

参考文献

keybindingに渡されるeventという引数は何者!? - Python

keybindingのメソッドに渡されるeventという引数は何者?

prompt_toolkit.key_binding.key_processor.KeyPressEventというやつ

https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/key_binding/key_processor.py#L412

  • data
  • key_processor
  • app: The current Application object.
  • current_buffer: The current buffer.
  • arg
  • arg_present
  • append_to_arg_count
  • cli
@kb.add('a')
def _(event):
    event.app.xxxxx

みたいにevent.appしておけばどうにかなる説

Application オブジェクトに全部格納してるから、それ見ればなんとかなるしょみたいな

ちょっと脱線

Applicaiton.current_bufferlayout.current_bufferらしい?

https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/application/application.py#L306

    @property
    def current_buffer(self):
        """
        The currently focused :class:`~.Buffer`.

        (This returns a dummy :class:`.Buffer` when none of the actual buffers
        has the focus. In this case, it's really not practical to check for
        `None` values or catch exceptions every time.)
        """
        return self.layout.current_buffer or Buffer(name='dummy-buffer')  # Dummy buffer.

あれか、起動する時にapp = Application(layout=xxxx)ってやったときのやつが取得できるってことか!!

また、特定のウィンドウにフォーカスがあうかどうかとかはhas_focusを使う

カーソルがある行のインデックス番号を取得 - Python

python-prompt-toolkitでカーソル行のインデックス番号を取得したいとき

prompt_toolkit.document.Document.cursor_position_row

カーソルがある行のインデックス番号が取得できる

0から数える

from prompt_toolkit.application import Application
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout.containers import Window
from prompt_toolkit.layout.controls import BufferControl
from prompt_toolkit.layout.layout import Layout
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.document import Document

from logging import getLogger, config
config.fileConfig("logger.conf")
logger = getLogger(__name__)


text = '\n'.join([str(i) for i in range(100)])

body = Window(BufferControl(Buffer(document=Document(text))), cursorline=True)

# キーバインディング
kb = KeyBindings()


@kb.add('q')
def _(event):
    event.app.exit()


@kb.add('o')
def _(event):
    # ここ!!!!
    logger.debug(event.current_buffer.document.cursor_position_row)


app = Application(
        layout=Layout(body),
        key_bindings=kb,
        full_screen=True)

app.run()

Enterを押すと、インデックス番号が取得できる

logger.confは以下のようになっている

[loggers]
keys=root

[handlers]
keys=fileHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=fileHandler

[handler_fileHandler]
class=FileHandler
level=DEBUG
; ハンドラで使用するフォーマッタを指定
formatter=simpleFormatter
; FileHandlerのコンストラクタへの引数
args=("log.log", "a+")

[formatter_simpleFormatter]
format=%(asctime)s:%(levelname)s %(message)s
; 時間の表示形式を設定
datefmt=%Y-%m-%d %H:%M:%S

Bufferのread_onlyを一時的にOFFにしたいとき - Python

Bufferのread_onlyAlways()だと、Bufferのtextとかdocumentとか何も変更できないから、Never()にする必要がある

form prompt_toolkit.filter import to_filter

# read_onlyをONにする
right.content.buffer.read_only = to_filter(True)

# read_onlyをOFFにする
right.content.buffer.read_only = to_filter(False)

使用例

from prompt_toolkit.application import Application
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout.containers import VSplit, Window
from prompt_toolkit.layout.controls import BufferControl
from prompt_toolkit.layout.layout import Layout
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.document import Document
from prompt_toolkit.filters import to_filter


text = '\n'.join([str(i) for i in range(100)])

left = Window(BufferControl(Buffer(document=Document(text), read_only=True)))
right = Window(BufferControl(Buffer(document=Document(text), read_only=True)))

# ウィンドウ
body = VSplit([
    left,
    Window(width=1, char='|'),
    right,
    ])


# キーバインディング
kb = KeyBindings()


@kb.add('tab')
def _(event):
    event.app.layout.focus_next()


@kb.add('s-tab')
def _(event):
    event.app.layout.focus_previous()


@kb.add('q')
def _(event):
    event.app.exit()


@kb.add('c-o')
def _(event):
    ## ここで、一時的にread_onlyをOFFにしている
    right.content.buffer.read_only = to_filter(False)
    right.content.buffer.document = Document('hello world!')
    right.content.buffer.read_only = to_filter(True)


app = Application(
        layout=Layout(body),
        key_bindings=kb,
        full_screen=True)

app.run()



以下、いろいろ調べたときのメモ

documentを変えるときだけ、read_onlyをFalseにする

Documnetのコンストラクタを見ると引数でもらったread_onluto_filter()に渡している

https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/buffer.py#L196

class Buffer(object):
    ...
    def __init__(self, completer=None, auto_suggest=None, history=None,
                 validator=None, tempfile_suffix='', name='',
                 complete_while_typing=False, validate_while_typing=False,
                 enable_history_search=False, document=None,
                 accept_handler=None, read_only=False, multiline=True,
                 on_text_changed=None, on_text_insert=None,
                 on_cursor_position_changed=None, on_completions_changed=None,
                 on_suggestion_set=None):

        # Accept both filters and booleans as input.
        enable_history_search = to_filter(enable_history_search)
        complete_while_typing = to_filter(complete_while_typing)
        validate_while_typing = to_filter(validate_while_typing)
        read_only = to_filter(read_only)
        ...

to_filter()ってなに

prompt_toolkit.filters.utilsにいた

https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/filters/utils.py

Trueを渡すと、prompt_toolkit.filters.base.Always()が返される。
Falseを渡すと、prompt_toolkit.filters.base.Never()が返される。

from __future__ import unicode_literals
from .base import Always, Never, Filter

__all__ = [
    'to_filter',
    'is_true',
]

_always = Always()
_never = Never()


def to_filter(bool_or_filter):
    """
    Accept both booleans and Filters as input and
    turn it into a Filter.
    """
    if not isinstance(bool_or_filter, (bool, Filter)):
        raise TypeError('Expecting a bool or a Filter instance. Got %r' % bool_or_filter)

    return {
        True: _always,
        False: _never,
    }.get(bool_or_filter, bool_or_filter)


def is_true(value):
    """
    Test whether `value` is True. In case of a Filter, call it.

    :param value: Boolean or `Filter` instance.
    """
    return to_filter(value)()

AlwaysとNeverってなに

prompt_toolkit.filters.baseにいた

https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/filters/base.py#L173-#L192

class Always(Filter):
    """
    Always enable feature.
    """
    def __call__(self):
        return True

    def __invert__(self):
        return Never()


class Never(Filter):
    """
    Never enable feature.
    """
    def __call__(self):
        return False

    def __invert__(self):
        return Always()

__call____invert__って何

__call__インスタンス生成以外で関数っぽく呼び出した時に呼ばれる

Pythonのクラスにおけるcallメソッドの使い方 - Qiita

__invert__は反転って意味らしい

よくわからなくなってきたけど、こうすればいいってことだ

form prompt_toolkit.filter import to_filter

# read_onlyにするとき
right.content.buffer.read_only = to_filter(True)

# read_onlyを解除するとき
right.content.buffer.read_only = to_filter(False)

has_focus(value)で渡したUIControl(Window)にフォーカスがあるか確認する - Python

指定したコントロールにフォーカスがあるかどうか

prompt_toolkit.layout.layout.Layout.has_focus()で確認できる

has_focus(value)

Check whether the given control has the focus.
:param value: UIControl or Window instance.

Reference — prompt_toolkit 2.0.6 documentation

WindowUIControlを渡す

使い方としてはキーバインディング

@kb.add('o')
def _(event):
    logger.debug(event.app.layout.has_focus(left_window))

って感じにする

まず、キーが押された時に、現在起動中のApplicationlayoutを取得する

こんなApplicationだったばあい

from prompt_toolkit.application import Application
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout.layout import Layout
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.layout.containers import VSplit, Window
from prompt_toolkit.layout.controls import BufferControl
from prompt_toolkit.document import Document
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.keys import Keys

from logging import getLogger, config
config.fileConfig("logger.conf")
logger = getLogger(__name__)


def get_text():
    return '\n'.join([chr(i) for i in range(65, 65+26)] * 3)


left_window = Window(BufferControl(Buffer(document=Document(get_text(), cursor_position=0), read_only=True)))
right_window = Window(BufferControl(Buffer(document=Document(get_text(), cursor_position=0), read_only=True)))
split_line = Window(width=1, char='|')

body = VSplit([
    left_window,
    split_line,
    right_window
    ])

# キーバインディング
kb = KeyBindings()
kb.add(Keys.Tab)(focus_next)
kb.add(Keys.BackTab)(focus_previous)


@kb.add('q')
def _(event):
    event.app.exit()


@kb.add('o')
def _(event):
    logger.debug(event.app.layout)


app = Application(
        layout=Layout(body),
        key_bindings=kb,
        full_screen=True)

app.run()

出力されるのは、

2018-11-02 07:39:37:DEBUG Layout(<prompt_toolkit.layout.containers.VSplit object 
at 0x1017b5be0>, current_window=Window(content=<BufferControl buffer=<Buffer
(name='', text='A\nB\nC\nD\nE\nF\n...') at 4319711808> at 4319711640>))

event.app.layoutでApplication()のコンストラクタのlayoutに指定した参照が取得できる。

指定のウィンドウにフォーカスがあるか調べる

左側のウィンドウにフォーカスがあるかどうかはLayout.has_focus()left_windowを渡せばいい

@kb.add('o')
def _(event):
    logger.debug(event.app.layout.has_focus(left_window))

左側にフォーカスがある時にoを押すとTrue、右側にあるときはFalse

基本的には、ウィンドウとかはインスタンスを変数に入れておけば良いのかな

このhas_focus()を使えば、特定のウィンドウにフォーカスがあるときのみ、キーバインドを実行するとかできるのかな?
fillterとか使えそう...?


logger.confは以下の通り

;ロガーを使用するときには以下のように記述する
;from logging import getLogger, config
;config.fileConfig("logger.conf")
;_logger = getLogger(__name__)

[loggers]
keys=root

[handlers]
keys=fileHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=fileHandler

[handler_fileHandler]
class=FileHandler
level=DEBUG
; ハンドラで使用するフォーマッタを指定
formatter=simpleFormatter
; FileHandlerのコンストラクタへの引数
args=("log.log", "a+")

[formatter_simpleFormatter]
format=%(asctime)s:%(levelname)s %(message)s
; 時間の表示形式を設定
datefmt=%Y-%m-%d %H:%M:%S

参考文献

vimっぽいページスクロール(c-dとc-u)をpython-prompt-toolkitで実装 - Python

python-prompt-toolkitを使って、ページのスクロールを実装したからメモメモ

以下の2つだけとりあえず、実装してみる

https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/key_binding/bindings/page_navigation.py#L48-L65

    handle('c-d')(scroll_half_page_down)
    handle('c-u')(scroll_half_page_up)

prompt_toolkit.key_binding.bindings.scrollscroll_half_page_downscroll_half_page_upの2つだ!

from prompt_toolkit.application import Application
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout.layout import Layout
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.layout.containers import Window
from prompt_toolkit.layout.controls import BufferControl
from prompt_toolkit.document import Document
# これ
from prompt_toolkit.key_binding.bindings.scroll import scroll_half_page_down, scroll_half_page_up


def get_text():
    return '\n'.join([chr(i) for i in range(65, 65+26)] * 3)


body = Window(BufferControl(Buffer(document=Document(get_text(), cursor_position=0), read_only=True)))

# キーバインディング
kb = KeyBindings()
# これ
kb.add('c-d')(scroll_half_page_down)
kb.add('c-u')(scroll_half_page_up)


@kb.add('q')
def _(event):
    event.app.exit()


app = Application(
        layout=Layout(body),
        key_bindings=kb,
        full_screen=True)

app.run()

できた!!!!!!!

c-dで半分下にスクロール、c-uで半分上にスクロール!!


できなかったやーつ

prompt_toolkit.key_binding.bindings.page_navigationload_vi_page_navigation_bindingsっていうのがあった

試してみる

以下のようにして、実行したら、c-dで半分下に移動ができなかった...
ただ呼ぶだけじゃだめなのかな?

from prompt_toolkit.application import Application
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout.layout import Layout
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.layout.containers import Window
from prompt_toolkit.layout.controls import BufferControl
from prompt_toolkit.document import Document
from prompt_toolkit.key_binding.bindings.page_navigation import load_vi_page_navigation_bindings


def get_text():
    return '\n'.join([chr(i) for i in range(65, 65+26)] * 3)


body = Window(BufferControl(Buffer(document=Document(get_text(), cursor_position=0), read_only=True)))

# キーバインディング
kb = KeyBindings()
load_vi_page_navigation_bindings()


@kb.add('q')
def _(event):
    event.app.exit()


app = Application(
        layout=Layout(body),
        key_bindings=kb,
        full_screen=True)

app.run()

ソースを見ると、ConditionalKeyBindingsインスタンスを返してるから、それをなんかするのかも?

https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/key_binding/bindings/page_navigation.py#L48-L65

def load_vi_page_navigation_bindings():
    """
    Key bindings, for scrolling up and down through pages.
    This are separate bindings, because GNU readline doesn't have them.
    """
    key_bindings = KeyBindings()
    handle = key_bindings.add

    handle('c-f')(scroll_forward)
    handle('c-b')(scroll_backward)
    handle('c-d')(scroll_half_page_down)
    handle('c-u')(scroll_half_page_up)
    handle('c-e')(scroll_one_line_down)
    handle('c-y')(scroll_one_line_up)
    handle('pagedown')(scroll_page_down)
    handle('pageup')(scroll_page_up)

    return ConditionalKeyBindings(key_bindings, vi_mode)

prompt_toolkit.key_binding.key_bindings.ContiditionalKeyBindings

viモードのときにしか聞かないのかな?