くろたんく雑記帳

日常とか、わんちゃんとか、機械学習とか、競プロとか、

MENU

Bio"Pack"athon2021 #10にて発表しました。


f:id:black_tank_top:20211013080956p:plain

はじめに

先日も報告しましたが、最近論文がPublishされました。

Kosugi T, Ohue M. Quantitative Estimate Index for Early-Stage Screening of Compounds Targeting Protein-Protein Interactions. International Journal of Molecular Sciences. 2021; 22(20):10925. https://doi.org/10.3390/ijms222010925

www.mdpi.com

サラッとした説明は昨日も書きました。 blacktanktop.hatenablog.com

今回の研究で、低分子化合物がタンパク質間相互作用(PPI)を標的にできそうかどうかを定量的にスコア化出来るモジュールQEPPIを開発したので、せっかくだからGitHubにあるだけでなく、研究者がさっと使えるように、pip install QEPPIとできるようにPyPIに登録してみました。自身としては初めての試みだったので、色々と調べながらやったことをまとめます。最初は開発モジュールの概要と紹介です。

また、PyPIに登録した後に、@antiplasticsさんからBio"Pack"athon2021 #10へ参加しませんか?ということで誘っていただき、本日発表を行ってきました。

QEPPIとは

タンパク質は、単体で機能するタンパク質もありますが、タンパク質とタンパク質とが相互作用(Protein-Protein interaction (PPI))することで特有の機能をします。(例えば、シグナル伝達・転写因子群による転写など)

近年、医薬品の開発において様々な標的の中で注目されているのがこのPPIです。しかしながら従来の標的(単体のタンパク質のリガンドポケット)とは大きく特性が異なるため、従来の指標ではPPIを標的にできそうかどうかを定量的にスコア化することが困難であることがこれまでの研究で知られており、求められています。

そこで私が所属しているラボで、低分子化合物がPPI(タンパク質間相互作用)を標的にできそうな度合いを定量的にスコア化できるQEPPIを開発しました。

開発について

GitHub上での開発

公開しているコードは、QEPPIを計算するモジュールのみですが、実際の開発では、学習(正確にいうと学習ではないので学習みたいな)コードも書いています。基本となる、QEDという従来医薬品における定量指標の論文に記述されている数式をベースにコードに起こして、作成しました。最終的に推論(これまた、正確に言うと推論ではないが推論みたいな)コードを公開したという経緯になります。

PyPIへの登録

  1. Package化に必要なモジュールのインストール
  2. Packageソースの作成
    1. init.pyの作成
    2. メインソースの作成
    3. MANIFEST.inの作成
    4. setup.pyの作成
    5. README.mdの作成 1 LICENSEの作成
  3. Twineを使ってPyPIに登録

事前準備

基本的に、パッケージ化に必要なsetuptoolsとwheel はinstall済みなことが多いですが、upgradeを行います。また後述しますがPyPIのupload用にtwineというモジュールを使うので、こちらもインストールしておきます。(モジュールの実行環境とは別にPyPIへのupload用にpyenvなどで環境を切り分けてから実行したほうが良いかと思います。)

 python3 -m pip install --upgrade setuptools wheel twine

Packageソースの作成

本題のPyPIの登録についてです。PyPIの登録は今回初めて行いました。ドキュメントはあるものの、ちょっとよくわからないというのが正直なところでした。なのでいろんなサイトを参考にしてやってみました。 基本いくつかのステップに分けられるのでステップに分けて説明します。

# QEPPIの例(本物とちょっと違うけど)
├── setup.py
├── MANIFEST.in
├── QEPPI  ← ここをモジュール名と合わせておくと吉
│   ├── __init__.py
│   ├── QEPPI.py
│   ├── constant.py
│   ├── util.py
│   └── version.py
├── README.md
├── LICENSE
  • setup.py : パッケージ作成情報を記述する。(詳細は後述)
  • MANIFEST.in : パッケージに追加、または除外するファイルを記述する。 例えばrequirements.txtなど。 LICENSE, README.md setup.pyなどは指定せずとも勝手にパッケージングされます。取り除きたい場合はexcludeであえて含ませないといったことが出来ます。また、setup.pyの仕様でrequirements.txtを使う場合はMANIFEST.in に書かないと怒られますので必ず書きましょう。
# MANIFEST.in
include *.md
exclude README.md
include requirements.txt
  • init.py : パッケージ基本情報を記述。 例えば以下のような感じ。 一番上のimportが結構重要です。
# __init__.py
from .<ファイル> import *  ← この行ポイント
__copyright__    = "Copyright (C) 2021 balcktanktop"
__version__      =  "0.1.10"
__license__      = "MIT"
__author__       = "くろたんく"
__author_email__ = "くろたんく@gmail.com"
__url__          = "http://github.com/blacktanktop/repository"
  • setup.py
    一番肝心のsetup.pyです。まず例です。
from setuptools import setup

exec(open("QEPPI/version.py").read())

with open("README.md", "r", encoding="utf-8") as fh:
    long_description = fh.read()

setup(
    name="QEPPI",
    version=__version__,
    author="blacktanktop,
    author_email="くろたんく@gmail.com",
    description="Calculation module of QEPPI",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/ohuelab/QEPPI/",
    license="MIT",
    packages=["QEPPI"],
    install_requires=[
        "rdkit-pypi>=2021.3.1.5",
        "numpy>=1.19.5",
        "pandas>=1.1.5",
    ],
    classifiers=[
        "Programming Language :: Python :: 3.7",
        "Programming Language :: Python :: 3.8",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
        "Topic :: Scientific/Engineering :: Chemistry",
        "Topic :: Scientific/Engineering :: Bio-Informatics",
    ],
)

name : パッケージ名称
version : バージョン
author : 著作者名
author_email : 著作者のメールアドレス
description : 簡単な説明
long_description : 詳細の説明 後述しますがREADME.mdファイルの内容をそのまま書き込むこともできます。
url : パッケージのWeb page GitHubリポジトリURLなど license : パッケージのライセンス(MITなど)
packages : モジュールリスト (Mainソースのディレクトリ名を指定する) install_requires : Dependencyのあるパッケージ
classifiers : 分類語のリスト 実際にPyPIで分類リストがあるのでそれを指定する。

冒頭の2行は

  • exec(open("QEPPI/version.py").read())
    • とするとverion.pyに書いてあるversion=0.1.10を読み込めるので、バージョンを変数で管理するためです。
with open("README.md", "r", encoding="utf-8") as fh:
    long_description = fh.read()
  • long_descriptionという変数にREADME.mdのテキストを突っ込むことで、 PyPIに登録したときの画面をREADME.mdと同じにするための方法です。

また、install_requiresをrequirements.txtで指定したい場合は、

def _requires_from_file(filename):
    return open(filename).read().splitlines()

のような関数を作っておいて

install_requires=_requires_from_file("requirements.txt")

などとします。

ただ、最近ではsetup.pyはシンプルにして、setup.cfgというconfigファイルに全部設定を書くというのが主流だそうです。 参考までに例を上げておきます。(マダよくわかってないがiniファイルっぽい作りのものでsetup()の括弧内のものを別のまとめ方でもできるよということです。)

[metadata]
name = package_name
version = attr: src.VERSION
description = short package description
long_description = file: README.md
keywords = foo, bar
license = MIT License
classifiers =
    Programming Language :: Python :: 3.7

[options]
zip_safe = False
include_package_data = True
packages = find:
scripts =
  bin/first.py
  bin/second.py
install_requires =
  pandas
  numpy; python_version == "3.7"

[options.package_data]
* = *.md

さて、これで下準備ができました!あとはビルドしてPyPIにupするだけです。

ちょっとテスト

ここまで出来ていると、以下のコマンドでinstallテストが可能です。

python setup.py install

pip listすればinstallが確認出来ると思います。

また、versionやその他の定義変数を設定している場合は以下のような確認もできます。

>>> import QEPPI
>>> QEPPI.__version__
'0.1.10'

ビルドの準備

ビルドするには、setuptoolsでビルドします。 setup.pyがある階層で以下のコマンドを叩くと完了です。

python setup.py sdist bdist_wheel
├── setup.py
├── MANIFEST.in
├── Makefile
├── QEPPI
│   ├── QEPPI.py
│   ├── __init__.py
│   ├── constant.py
│   ├── util.py
│   └── version.py
├── QEPPI.egg-info
│   ├── PKG-INFO
│   ├── SOURCES.txt
│   ├── dependency_links.txt
│   ├── requires.txt
│   └── top_level.txt
├── build
│   ├── bdist.macosx-10.12-x86_64
│   └── lib
│       └── QEPPI
│           ├── QEPPI.py
│           ├── __init__.py
│           ├── constant.py
│           ├── util.py
│           └── version.py
├── dist
│   ├── QEPPI-0.1.10-py3-none-any.whl
│   └── QEPPI-0.1.10.tar.gz
├── README.md
├── LICENSE

最初のディレクトリと比べると、.egg-infobuild/dist/ が作られます。最終的に登録するのはdistの中身になります。

PyPIへのupload

早速登録したいのですが、まずいわゆるユーザー登録の必要があります。そして、PyPIにはテスト用の環境と、本番用の環境があります。

test.pypi.org

pypi.org

また、setup.pyのコマンドでupしてもいいのですが、今回はtwineを使います。(最初にインストールした) github.com

また、README.mdをそのままlong descriptionに設定している場合は、本当に今のREADME.mdで良いかをしっかりと検討して、やりきってからupに進んだほうが良いです。というのもTest.PyPIPyPIもupのやり直しができません。versionを同じままで、descriptionだけ変えたいということができない(と思う)ので気をつけましょう。基本的に変更はversionを少しでもいいから上げる(1.0.0なら1.0.1とか) 削除しても同じ名前だと記録が残っているので、削除してもダメです。

Test.PyPI (練習用)へのupload

README.mdのチェックが終わったら、upします。 以下のコマンドでTest.PyPIにup出来ます

twine upload --repository-url https://test.pypi.org/legacy/ dist/*

パスが求められるのでユーザー登録したときのusernameとpassを入れましょう。

upが終わったら、出力されるURLを見に行きましょう

test.pypi.org

こんな感じなので、

pip install -i https://test.pypi.org/simple/ QEPPI

のようにコマンドを叩くと、ローカル環境にインストールされます。 インストール後に、適宜問題が無いかチェックします。必要があれば、テストコードを走らせる等しましょう。

PyPI (本番)へのupload

ここまで来てしまえば、同じ様に本番にupします。

twine upload --repository-url https://pypi.org/legacy/ dist/*

こちらでも、パスが求められるのでユーザー登録したときのusernameとpassを入れましょう。

upが終わったら、出力されるURLを見に行きましょう。

pypi.org

こんな感じになって、PyPI登録出来ました!!!

あとは、適当な環境で

pip install QEPPI

のようにインストールをすると、依存関係もインストールされるのが確認できます。

f:id:black_tank_top:20211004193203p:plain
pip installしたときの様子

Makefileによるタスクフロー化

これからバージョンを上げていくときに便利なようにMakefileを使って、タスク化してしまいましょう。

その前に、毎回、パスを入れるのも面倒なのとセキュリティ的にもちょっと微妙なので、API tokenを使って登録出来るようにします。アカウント設定の下の方から取得出来るのでそれぞれ取得して下さい。

そうしたら、~/.pypircを作って下さい。中身は以下のようにしましょう。usernameをtokenとするとpassはAPI tokenと認識するようです。(ちなみにここに、自分のusernameとpassを書いてもよいがtokenのほうが良いですね)

[distutils]
index-servers =
  pypi
  testpypi

[pypi]
username = __token__
password = pypi-~~~tokenを入れる

[testpypi]
username = __token__
password = pypi-~~~tokenを入れる

これで設定はバッチリです。

では、話を戻して、Makefikeの例を見ていきましょう。

私はコマンドを手で叩くのが一周したら、必ずこの様なMakefileを書きます。Rakeでも良いですが環境を作る必要がないのでMakefileをタスク実行ツールととして使っています。

make clean : 一度、Buildするとegg.Infoとかdistの前のバージョンが残ったままになってしまいます。なので、まずcleanで過去のパッケージを消します。単純にrm -rfで消します。
make build : ビルドしてupするためのdistを作ります。
make test-deploy : Test.PyPIにupします。
make deploy : PyPIにupします。

この様にMakefileでタスク化しておくとしばらく経ってからのバージョン変更の際にも、記憶に優しく実行できますね。

あとはパッケージをUpdateしていきましょう!

ポイント

PyPIのdescriptionとGitHubのREADME.mdが一致しないとなんか気持ち悪いですが、PyPIは過去のバージョン、GitHubがdevelopバージョンと割り切って、GitHubで開発を進めて、ある程度機能が備わったらPyPIにバージョンを上げたものをuploadといった様にするのが良いかと思います。

今回の内容をまとめたスライド

研究概要 + PyPI登録に関連する資料を作成しました。 Speaker Deckにupしてありますので興味ある方はそちらも御覧ください。

speakerdeck.com


終わりに

今回は、自分が開発したパッケージをpip install QEPPIのようにインストール出来るようにするために、PyPIにuploadする手順をまとめました。初めて行った作業で、まだわかっていない部分もありますが、だいたいこの手順で行えばそこまで問題は起きないかなと思います。

今回改めて、普段何気なく使っているOSSツールがとても大変な労力をかけてやられているのだと実感しました。

また、質問を受けて思ったのは、 * Condaでの公開方法との違い * PyPIへuploadするときのテスト関係 * プラットフォーム別のバイナリーデータについて ここらへんは、今後、きちんと調べてきたいと思います。

資料作って、話し、質問を受けるということをするととても勉強になり、参加してよかったなと思いました。

参考にしたブログ等

公式以外で参考にしたものは以下です。ありがとうございました。

zenn.dev

www.m3tech.blog

qiita.com

qiita.com

qiita.com

参考文献

導入でPPIの説明に出てきた内容の文献はこちらです。

  1. Shin, W.-H.; Kumazawa, K.; Imai, K.; Hirokawa, T.; Kihara, D. Current challenges and opportunities in designing protein–protein interaction targeted drugs. Adv. Appl. Bioinform. Chem. 2020, 13, 11–25, doi:10.2147/AABC.S235542.

  2. Morelli, X.; Bourgeas, R.; Roche, P. Chemical and structural lessons from recent successes in protein-protein interaction inhibition (2P2I). Curr. Opin. Chem. Biol. 2011, 15, 475–481, doi:10.1016/j.cbpa.2011.05.024.

  3. Bickerton, G.R.; Paolini, G. V.; Besnard, J.; Muresan, S.; Hopkins, A.L. Quantifying the chemical beauty of drugs. Nat. Chem. 2012, 4, 90–98, doi:10.1038/nchem.1243.

  4. Jumper, J.; Evans, R.; Pritzel, A.; Green, T.; Figurnov, M.; Ronneberger, O.; Tunyasuvunakool, K.; Bates, R.; Žídek, A.; Potapenko, A.; et al. Highly accurate protein structure prediction with AlphaFold. Nature 2021, doi:10.1038/s41586-021-03819-2.