Python

ディレクトリツリー作成便利ツールを作成してPyPIで公開するまで

業務用+社内共有目的で Python製ディレクトリツリー作成ツール「wintree」 を自作・PyPI公開。

ディレクトリツリー作成便利ツールを作成してPyPIで公開するまで

導入

最近業務でディレクトリツリーを作成する機会が多かったのですが、 Windows標準のtreeコマンドってオプション機能とか全然なくて使いづらいよね。。

ってことで休日の暇つぶしに業務で使う用のディレクトリツリー作成ツールを作成して どうせだったら他の社員にも使ってもらいたかったのでPyPIで公開して pip installで簡単に導入できるようにしてみました。

今まで自分でライブラリを作ったことがなかったので備忘録として記事を書きます。

対象読者

  • ディレクトリツリーを一瞬で作成したい人
  • 自分でPythonライブラリを作ってみたい人
  • 社内適当ツールをサクッと作りたい人
  • モダンなPythonライブラリ作成方法を知りたい人

Tl;DR

  • Windowsのtreeコマンドが物足りないので、業務用+社内共有目的で Python製ディレクトリツリー作成ツール「wintree」 を自作・PyPI公開。
  • モダンなPython開発手法を採用:poetry+pyproject.tomlベースでセットアップ、ビルド、バージョン管理、公開まで一括管理。
  • ライブラリ公開の敷居は低いけど、“誰かが使う”意識は大切。品質はきちんと担保しようという自戒。

ライブラリの紹介

今回の記事はライブラリの作成からPyPI公開までの流れをメインにするので簡単に紹介します。

Windows標準treeコマンドを拡張するという意味でライブラリ名は wintree にしました。 ただディレクトリツリーを楽に作成するためのライブラリ&CLIツールです。 https://github.com/harumiWeb/wintree

使い方

インストール

PyPIで公開しているので、pip installでインストールできます。

pip install wintree

ライブラリとして使用する

tree関数を実行すると引数で指定されたパスをルートにディレクトリツリーを作成します。

main.py
import wintree

print(wintree.tree())
出力結果
📂 root: .
└── 📄 main.py

引数オプションとしては除外ディレクトの指定や絵文字の有り無し、ファイル拡張子の指定をすることができます。自分が求めていた機能ですね。

print(wintree.tree(use_emoji=False, ignore_dirs=[".venv"], filter_exts=[".py", ".txt"]))
出力結果
root: .
└── main.py

他には使うか分かりませんが、社内アプリでファイルの階層構造をGUIにする機会がたまにあったので、ツリーの情報をJSON形式で出力する機能なんかもあります。

CLIツールとして使用する

先ほどのライブラリはおまけみたいなもので、本命はこちらです。 本家treeコマンドのようにCLIでも実行することができるようにしました。

PS C:\dev\go\todo_app> wintree .
📂 root: .
├── 📁 app/
│   └── 📁 models/
│       ├── 📄 base.go
│       └── 📄 users.go
├── 📁 config/
│   └── 📄 config.go
├── 📄 config.ini
├── 📄 go.mod
├── 📄 go.sum
├── 📄 main.go
├── 📁 utils/
│   └── 📄 logging.go
├── 📄 webapp.log
└── 📄 webapp.sql

自分が欲しかった除外ディレクト指定なんかもコマンド一発でできるようになりました。

PS C:\dev\go\todo_app> wintree . --no-emoji --exclude utils config
root: .
├── app/
│   └── models/
│       ├── base.go
│       └── users.go
├── go.mod
├── go.sum
├── main.go
├── webapp.log
└── webapp.sql

他にも色々できるので、気になる人はGitHubを御覧ください。🙆

ライブラリ兼CLIツールの作り方

ちょっと古い作り方だとsetup.pyを使った作成方法がよくヒットしますが、 現在はpyproject.tomlベースで作成するのが主流になっています。

そしてモダンなパッケージ管理ツールである poetry を使うことで、 pyproject.tomlを使った効率的なライブラリ開発をすることができます。

https://python-poetry.org/

poetryをまだ導入していない方はインストールしましょう。 poetryはライブラリ作成用途でなくともパッケージ管理ツールとしても優秀なのでおすすめです。

pip install poetry

プロジェクトの作成

poetryでライブラリを作成する場合は次のコマンドで簡単セットアップできます。

poetry new project_name

既存のプロジェクトにpoetryを組み込むには以下のコマンドを実行します。

poetry init

poetry new fugaと実行すると以下のようなディレクトリ構造が生成されます。 srcディレクトリ配下にライブラリのプログラムを作成していく感じになります。

📂 root: .
├── 📁 src/
│   └── 📁 fuga/
│       └── 📄 __init__.py
├── 📁 tests/
│   └── 📄 __init__.py
├── 📄 pyproject.toml
└── 📄 README.md

pyproject.tomlの設定

pyproject.tomlはデフォルト生成では以下のようになっています。 ライブラリをインストールするときはpoetry add ~コマンドを実行することで、 dependenciesに自動的に依存ライブラリが記述されていきます。 このへんはpackage.json然り似たようなものなのですぐ馴染むと思います。

pyproject.toml
[project]
name = "fuga"
version = "0.1.0"
description = ""
authors = [
    {name = "harumi",email = "hogehoge@fuga.com"}
]
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
]

[tool.poetry]
packages = [{include = "fuga", from = "src"}]


[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"

CLIとして使えるようにする

普通にimportしてpyファイル内で使うだけなら特に設定不要なのですが、 コマンドプロンプトから実行できるようにする場合は少し設定が必要です。

私はCLIで実行する用にsrcディレクトリ配下にcli.pyを作成しました。 中身はargparseモジュールでコマンドライン引数を定義して、ライブラリの関数を実行するだけの簡単な処理です。

cli.py
import argparse
from . import tree_cli, list_files, tree_to_json

def main():
    parser = argparse.ArgumentParser(description="A tool to display directory trees or file listings")
    parser.add_argument("path", help="Path to the root directory")
    parser.add_argument("--no-emoji", action="store_true", help="Hide emojis")
    parser.add_argument("--exclude", nargs="*", default=[], help="Names of directories to exclude (partial match)")
    parser.add_argument("--ext", nargs="*", default=[], help="File extensions to filter (e.g., .py, .txt)")
    parser.add_argument("--no-tree", action="store_true", help="Print file paths only (no tree view)")
    parser.add_argument("--abs", action="store_true", help="Use absolute paths for file listing")
    parser.add_argument("--json-output", help="Path to save JSON file. If specified, saves tree to JSON.")
    parser.add_argument("--show-meta", action="store_true", help="Include metadata in JSON output")

    args = parser.parse_args()

    if args.json_output:
        tree_to_json(
            root_dir=args.path,
            save_path=args.json_output,
            ignore_dirs=args.exclude,
            filter_exts=args.ext,
            show_meta=args.show_meta
        )
        print(f"✅ JSON saved to {args.json_output}")
    elif args.no_tree:
        print(list_files(args.path, ignore_dirs=args.exclude, filter_exts=args.ext, absolute_paths=args.abs))
    else:
        tree_cli(args.path, use_emoji=not args.no_emoji, ignore_dirs=args.exclude, filter_exts=args.ext)

if __name__ == "__main__":
    main()

ミソなのは次の設定で、pyproject.tomlに上記で設定したcli.pymain関数をコマンドラインから呼び出せるようにします。

pyproject.toml
[tool.poetry.scripts]
wintree = "wintree.cli:main"

このように設定することで、ライブラリをインストールしたPCは独自のコマンドを実行できるようになります。 めちゃくちゃ簡単!!

wintree .

プロジェクトのビルド

ビルドは以下のコマンドを実行します。 実行すると/distディレクトリが作成され.whl.tar.gzファイルが生成されます。

poetry build

継続的にライブラリを開発する際、バージョンを自動で上げてくれるコマンドもあります。

poetry version patch # パッチ 0.0.1
poetry version minor # マイナーバージョンアップ 0.1.0
poetry version major # メジャーバージョンアップ 1.0.0

ライブラリの公開

poetryPyPIへの公開も非常に楽ちんです。

公開する際は事前にPyPIのユーザー登録とAPIトークンの発行を済ませておく必要がありますが、この記事では手順は割愛します。

poetryにPyPIのAPIトークンを設定するには以下のコマンドを実行します。

poetry config pypi-token.pypi YOUR_PYPI_API_TOKEN

トークンの設定が終わればあとは公開コマンドを実行するだけです。 自分の作ったライブラリを世の中に公開しちゃいましょう🐍

poetry publish

その他、pyproject.tomlに設定しておくと良かったもの

コマンドの一括実行

Node.jsのpackage.jsonではscriptsキーにコマンドを設定したりしますが、あれと同じことをやるならtaskipyがオススメです。

pip install taskipy

インストール後にpyproject.toml[tool.taskipy.tasks]という項目で設定を記述することで、 poetry run task {タスク名}でコマンドを一括実行できるようになります。

バージョンアップからPyPI公開までや、pytestの実行時のコマンドライン引数を設定しておくと開発がはかどります。

pyproject.toml
[tool.poetry.group.dev.dependencies]
pytest = "^8.4.1"
pytest-mock = "^3.14.1"
taskipy = "^1.14.1"
pytest-clarity = "^1.0.1"
pytest-cov = "^6.2.1"

[tool.pytest.ini_options]
addopts = "--strict-markers --strict-config"
testpaths = ["tests"]
filterwarnings = [
    "ignore::DeprecationWarning",
    "ignore::PendingDeprecationWarning",
]

[tool.taskipy.tasks]
test = "pytest -vv --cov=wintree --cov-report=term-missing --cov-report=html"
release-patch = "poetry version patch && poetry build && poetry publish"
release-minor = "poetry version minor && poetry build && poetry publish"
release-major = "poetry version major && poetry build && poetry publish"

PyPIに公開してみて

今回作成したライブラリはディレクトリツリーを作成するための非常に簡単なライブラリで、社内の人くらいしか使わないだろうと軽い気持ちでPyPIに公開しましたが、

公開から3日足らずで約3000回ほどダウンロードされていて、世界の広さを感じました..

気軽にPyPIに公開しよう!というスタンスの記事を書いていますが、やっぱり目に見えない世界の誰かがインストールして使うので、ある程度はちゃんとしたものを作りましょう。(自戒の念)

ちなみにライブラリのダウンロード数は以下のサイトから調べることができます。 ライブラリ開発のモチベになるのでおすすめです。 https://pepy.tech/

終わりに

今回はPythonライブラリ&CLIツールの作り方を簡単に紹介してみました。

GORustで適当ツールを作るのもいいですが、社内の他のメンバーにも使ってもらいたいなどの要件ではPythonもかなり優秀なのではないかと思った7月でした。

次にライブラリを自作するときは、もっと需要があってカッコ良さそうなものに挑戦しようと思います。

それではまた👋

profile

ハルミ

1997年生まれ。某メーカーの新米DX担当。
三度の飯より効率化が好き。
プログラミングにハマり、Webエンジニアを目指す。
現在React/Next.jsを学習しています🚀