手元の自作pythonライブラリを競プロでも活用したいのでツールを作った

概要

競プロなど単一のファイル提出が求められるケースに対応するために、手元の複数ファイルにまたがるpythonコードを組み合わせて単一の提出ファイルを作成するコマンドラインツールsinglueを作成しています。 2022年9月21日時点のバージョン(v0.1.5)での実行方法について紹介します。

github.com

背景

手元のディレクトリ内で定義されている関数やクラスをimportしながら作成したプログラムをオンラインジャッジの環境に提出すると手元でしか定義されていないので当然エラーが出ます。 そのため提出するファイルに、コピペしたり、あらかじめ使うであろうものを記述しておいたり、スニペットとして展開できるようにしたりといった方法があるかと思います。 いずれにおいても手動のオペレーションでミスする、量の多いテンプレートの見た目に圧倒される、スニペットのメンテナンスをミスする(改良した部分がスニペットには反映されていない)、といった経験があるのではないでしょうか(私は3つともありました)。

そこで1ファイルが巨大にならないようにライブラリとして整備しつつ、コピペミスによるエラー、実行環境の差異によるエラーが起こらないようにsinglueというコマンドラインツールを作成しました。

イメージ

概念を図で表すと以下のようなイメージです。

手元のファイル群(青枠で囲った部分)から提出用ファイル(赤枠部分、output.py)を生成します。

使用方法

インストール方法、実行例について説明します。

インストール方法

astモジュールにpython3.9以降に追加されたunparseに依存しているためpython3.9以降の環境が必要になります。 この環境が既にあればpip install singlueで事足りますが、無い場合は以下の手順でインストール出来るかと思います。

  • pyenvでpython3.9を追加
  • pipxをインストール
  • pipx install singlue

実行例

前提

以下のようなディレクトリ構造を仮定します。

$ tree
.
├── library.py
└── main.py

また各ファイルの内容は以下のものとします。

main.py

from library import one, two, Three


assert one() + two() == Three().three()

library.py

def one() -> int:
    return 1


def two() -> int:
    return 2


class Three:
    def __init__(self):
        self.value = 3

    def three(self):
        return self.value

実行コマンド

singlue main.py > output.py

上のコマンドを実行すると以下の内容のoutput.pyというファイルが作成されます。 ジャッジ環境において解決できずエラーを起こすimport文が削除され、代わりにimportする対象のクラスや関数が埋め込まれているのが確認出来ます。

なお、説明の都合上ファイル名をmain.pylibrary.pyとしていますがこの命名である必要はないです。 またライブラリとしているファイルはmain.pyと同一のディレクトリ階層にあるという制約はありますがlibrary.pyに類するファイルが複数であっても動きます。

output.py

def one() -> int:
    return 1
def two() -> int:
    return 2
class Three:

    def __init__(self):
        self.value = 3

    def three(self):
        return self.value
assert one() + two() == Three().three()

課題

混み入った依存関係が必要になるケースは少ないのではないかと感じていますが、多段の依存関係の解決(ファイル内、ファイル間)が出来ないのが現在の課題です。

最後に

コンテストにぶっつけ本番で使用することはおすすめしません。日々の精進等で試していただけると嬉しいです。

課題等を報告する場合はGitHubのIssues(https://github.com/kawagh/singlue/issues)にてお願いします。