とりあえずコードに言及するコマンドをpyinspectに追加した

昨日のこれらの記事の暫定的な実装をpyinspectに追加した。

github.com

$ pyinspect quote starlette.endpoints:L99

~/venvs/my/lib/python3.7/site-packages/starlette/endpoints.py

class WebSocketEndpoint:

    encoding = None  # May be "text", "bytes", or "json".

# ...

    def decode(self, websocket: WebSocket, message: Message) -> typing.Any:

        if self.encoding == "text":

# ...

            try:
                return json.loads(text)
            except json.decoder.JSONDecodeError:
                await websocket.close(code=status.WS_1003_UNSUPPORTED_DATA)

はい。

$ pyinspect quote argparse:L1900

/usr/lib/python3.7/argparse.py

class ArgumentParser(_AttributeHolder, _ActionsContainer):
    """Object for parsing command line strings into Python objects.

    Keyword Arguments:
        - prog -- The name of the program (default: sys.argv[0])
        - usage -- A usage message (default: auto-generated from arguments)
        - description -- A description of what the program does
        - epilog -- Text following the argument descriptions
        - parents -- Parsers whose arguments should be copied into this one
        - formatter_class -- HelpFormatter class for printing help messages
        - prefix_chars -- Characters that prefix optional arguments
        - fromfile_prefix_chars -- Characters that prefix files containing
            additional arguments
        - argument_default -- The default value for all arguments
        - conflict_handler -- String indicating how to handle conflicts
        - add_help -- Add a -h/-help option
        - allow_abbrev -- Allow long options to be abbreviated unambiguously
    """
# ...

    def _parse_known_args(self, arg_strings, namespace):
        # replace arg strings that are file references
        if self.fromfile_prefix_chars is not None:

# ...

        def consume_optional(start_index):

# ...

                    # successfully matched the option; exit the loop
                    elif arg_count == 1:
                        stop = start_index + 1
                        args = [explicit_arg]
                        action_tuples.append((action, args, option_string))

まだ、docstringは全く省略できない。

追記

ちょっとだけ色々修正を加えた。astゴニョゴニョするときに、引数やdocstringは物切りにならないようにとか、下側で別の関数やメソッドの定義は表示されないようにとか、ちょっとだけ頑張った。

気になる点のメモ

まだまだ気になる点は幾つかあって、それを直したいなーと思う部分も存在するのでメモしておく。

  • docstringは全体ではなくsummaryだけを表示したい
  • やっぱり呼び出しが面倒
  • 指定した行に対してhighlightさせたい(強調表示させたい)
  • やっぱり実装コードに対するリンクが貼りたい

docstringは全体ではなくsummaryだけを表示したい

長いdocstringは引用したい部分自体の意図を弱めてしまう。完全に使い方の問題だけどdocstringとかが長いコードが嫌いになるかも。

今回のような表示でのdocstringの位置付けの問題ではある。ある全体のうちの一部分に目を向けてほしいという文脈での表示なのだけどdocstringは全体について語った全文。summaryとdescriptionは分かれてほしいという感じかも(summaryは雰囲気を掴めるので良い)。

docstringは、ある関数の一部分だけに興味があるときと、複数の関数同士の関係について考えたいときに、煩く感じたりする。その関数だけに興味がある場合は意味があることが多いのだけれど。

そういう意味ではopenAPI schemaがdescriptionとsummaryを持っているのは正しいのだよなー。真面目に使う分には。真面目に使うの面倒過ぎるけど。

現実的には先頭行がsummaryっぽくなっていたら表示とかにする?そういう仮定に基づいて先頭N行だけを表示とかにしちゃっても良いかもしれない。

まぁこれは関数定義の引数部分も同様の話があるのだけれど。

やっぱり呼び出しが面倒

うーん、やっぱりエディタから直接呼び出したい感じがする。とはいえ特定のエディタと過度に結合したプラグインとかは作りたくないから良い感じにlanguage serviceのクライアントの実装を使い回す形で個人的なサービスを提供するような層を作れないかな(individual service provider的なもの)。

微妙にむずかしいのは通常の言語に対するLSサーバーがある一方で、この個人的なLSサーバー的な何かをどういう粒度で立ち上げて置くのが良いかと言うような話。単純にはglobalに1つのあれば良いような気もする?最終的にはyamlに対するlintとか補完とかにも対応したりしたいのだよなー(ちょっと別枠の話かもだけれど)。

そろそろまじめにyet another language service frameworkみたいなものを考えてみても良いかもしれない。

emacsでいう xxx-at-point みたいな事ができれば良いのだよなー。ただ指定するのはpointに限らないかもしれない。あるいはsend context的な意味合いで文脈に従って何かを投げるみたいな操作。

指定した行に対してhighlightさせたい(強調表示させたい)

引用の意図を伝えたいという性質上、親階層のものはflavorでしか無いし。何ならその周辺行もflavorでしかない。指定した行の上下数行を表示するという形にした一方で、見た瞬間に中央部分に目が行くということでもなさそうなので何らかの強調ができると良いのかもしれない。

markdownのcodeにhightlightみたいなオプションあったっけ?とはいえ特定の実装にはあまり依存したくないという気持ちもある。

あと、上下何行表示するかをオプションで取りたいかもしれない。

  • 親階層部分で何行表示するか
  • 指定した行の周辺部分で何行表示するか

が選択可能であれば良いんだろうか?

やっぱり実装コードに対するリンクが貼りたい

やっぱりリンクも欲しい。値の参照とコピー値の扱いじゃないけれど、どうしてもこの引用のコマンドで出力した瞬間に情報がforkされるような感覚がある。forkされるということはupstreamの変更に追随されなくなるかもしれないということではあって。それをどうにかしたい。

ただ例えばpip showから取れるのはhome-pageの部分だけだし、editable modeでインストールしてないとsite-packagesにしかないのだよなー。

例えばこういう感じ。

Name: Jinja2
Version: 2.10
Summary: A small but fast and easy to use stand-alone template engine written in pure python.
Home-page: http://jinja.pocoo.org/
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
License: BSD
Location: $HOME/venvs/my/lib/python3.7/site-packages
Requires: MarkupSafe
Required-by: Sphinx

ただ Home-page に飛べるのは便利なので pyinspect webpage というサブコマンドも作った。

$ pyinspect webpage --show-only jinja2
http://jinja.pocoo.org/

# web browserで開く
$ pyinspect webpage jinja2

強制されない状態なら地味にwebbrowserモジュールが好きだったりする。