Open7

pypicloudで遊ぶ

Takei KazuyaTakei Kazuya

主な目的

  • 自分が使ってるpypicloud環境のおさらい
  • 途中段階の記事化

スクラップ内の前提

  • Arch Linux
  • Python 3.9
  • Poetry
Takei KazuyaTakei Kazuya

pypicloudとは

基本情報

超概要

いわゆるPyPIクローン。「プライベートなPyPI環境の作成」「ローカルでのPythonパッケージのキャッシュ」などが可能になる。

内部コンポーネントにいくつかのクラウドサービスを利用できるようなインターフェースを備えているのが特徴。 [1]
このため、インスンタンス系のサービスに依存しない形での独自PyPI環境が用意。

脚注
  1. 実際にいくつかのサービス向けの実装が同梱されている ↩︎

Takei KazuyaTakei Kazuya

ローカル向けにPyPIを用意する

インストール

素のパッケージではHTTPサーバー系ライブラリがないので、extraとしてserverを追加。[1]

$ poetry init

(面倒なので、全部Enter)

$ poetry add 'pypicloud[server]'

環境の準備

とりあえずのローカル用なので、シンプルな設定にする。

$ poetry run pypicloud-make-config config.ini
[1] dev
[2] test
[3] prod
What is this config file for? 1
[1] s3
[2] gcs
[3] filesystem
[4] azure-blob
Where do you want to store your packages? 3
Admin username? admin
Password:
Password:
Config file written to 'config.ini'

起動

Pyramid製なので、pserveでそのまま起動可能

$ poetry run pserve config.ini
INFO 2021-02-23 14:54:36,375 [pypicloud.cache.base] Cache is empty. Rebuilding from storage backend...
INFO 2021-02-23 14:54:36,375 [pypicloud.cache.base] Cache repopulated
Starting server in PID 2739933.
Serving on http://0.0.0.0:6543
脚注
  1. waitressがインストール対象になる ↩︎

Takei KazuyaTakei Kazuya

簡単に動作確認する

使う

ブラウザでアクセスしてみる

当然だけど、初期状態では空っぽ

pip 経由でアクセスしてみる

python -m venv .venvで別環境を用意して、インデックスに起動したサーバーを指定しつつpypicloudをインストールしてみる。

$ pip install -i http://localhost:6543/simple/ 'pypicloud[server]'

(中略)

pypicloud
    Running setup.py install for beaker ... done
    Running setup.py install for pyramid-beaker ... done
Successfully installed Jinja2-2.11.3 MarkupSafe-1.1.1 PasteDeploy-2.1.1 SQLAlchemy-1.3.23 beaker-1.11.0 boto3-1.17.13 botocore-1.20.13 certifi-2020.12.5 cffi-1.14.5 chardet-4.0.0 cryptography-3.4.6 distlib-0.3.1 hupper-1.10.2 idna-2.10 jmespath-0.10.0 passlib-1.7.4 paste-3.5.0 plaster-1.0 plaster-pastedeploy-0.7 pycparser-2.20 pypicloud-1.1.7 pyramid-1.10.7 pyramid-beaker-0.8 pyramid-duh-0.1.2 pyramid-jinja2-2.8 pyramid-rpc-0.8 pyramid-tm-2.4 python-dateutil-2.8.1 requests-2.25.1 s3transfer-0.3.4 six-1.15.0 transaction-3.0.1 translationstring-1.4 urllib3-1.26.3 venusian-3.0.0 waitress-1.4.4 webob-1.8.7 zope.deprecation-4.4.0 zope.interface-5.2.0 zope.sqlalchemy-1.3

特に問題なくインストールできる。

サーバーの配下フォルダの様子を見る

$ ls -l
-rw-r--r-- 1 attakei attakei  1389 Feb 23 14:42 config.ini
-rw-r--r-- 1 attakei attakei 24576 Feb 23 14:54 db.sqlite
drwxr-xr-x 2 attakei attakei     6 Feb 23 14:54 packages
-rw-r--r-- 1 attakei attakei 48737 Feb 23 14:26 poetry.lock
-rw-r--r-- 1 attakei attakei   339 Feb 23 14:26 pyproject.toml

db.sqlitepackages が増えている。が、出番はちょっと先

Takei KazuyaTakei Kazuya

ローカルでキャッシュする

Before

$ time .venv/bin/pip install -i http://localhost:6543/simple/ 'pypicloud[server]'

(中略)
Looking in indexes: http://localhost:6543/simple/
(中略)
Successfully installed Jinja2-2.11.3 MarkupSafe-1.1.1 PasteDeploy-2.1.1 SQLAlchemy-1.3.23 beaker-1.11.0 boto3-1.17.13 botocore-1.20.13 certifi-2020.12.5 cffi-1.14.5 chardet-4.0.0 cryptography-3.4.6 distlib-0.3.1 hupper-1.10.2 idna-2.10 jmespath-0.10.0 passlib-1.7.4 paste-3.5.0 plaster-1.0 plaster-pastedeploy-0.7 pycparser-2.20 pypicloud-1.1.7 pyramid-1.10.7 pyramid-beaker-0.8 pyramid-duh-0.1.2 pyramid-jinja2-2.8 pyramid-rpc-0.8 pyramid-tm-2.4 python-dateutil-2.8.1 requests-2.25.1 s3transfer-0.3.4 six-1.15.0 transaction-3.0.1 translationstring-1.4 urllib3-1.26.3 venusian-3.0.0 waitress-1.4.4 webob-1.8.7 zope.deprecation-4.4.0 zope.interface-5.2.0 zope.sqlalchemy-1.3
.venv/bin/pip install -i http://localhost:6543/simple/ 'pypicloud[server]'  6.79s user 0.58s system 57% cpu 12.913 total

設定を変更する

config.ini
+pypi.fallback = cache
+
 pypi.default_read =
     everyone
 pypi.default_write =
     authenticated
+pypi.cache_update =
     everyone

 pypi.storage = file
 storage.dir = %(here)s/packages

After

pipを実行すると、pypicloudのサーバーからwheelをダウンロードしている。
Beforeと比較すると速くなってはいない。[1]

$ time .venv/bin/pip install -i http://localhost:6543/simple/ 'pypicloud[server]'
Looking in indexes: http://localhost:6543/simple/
Collecting pypicloud[server]
  Downloading http://localhost:6543/api/package/pypicloud/pypicloud-1.1.7-py2.py3-none-any.whl (537 kB)
     |████████████████████████████████| 537 kB 57.5 MB/s
(中略)
Successfully installed Jinja2-2.11.3 MarkupSafe-1.1.1 PasteDeploy-2.1.1 SQLAlchemy-1.3.23 beaker-1.11.0 boto3-1.17.13 botocore-1.20.13 certifi-2020.12.5 cffi-1.14.5 chardet-4.0.0 cryptography-3.4.6 distlib-0.3.1 hupper-1.10.2 idna-2.10 jmespath-0.10.0 passlib-1.7.4 paste-3.5.0 plaster-1.0 plaster-pastedeploy-0.7 pycparser-2.20 pypicloud-1.1.7 pyramid-1.10.7 pyramid-beaker-0.8 pyramid-duh-0.1.2 pyramid-jinja2-2.8 pyramid-rpc-0.8 pyramid-tm-2.4 python-dateutil-2.8.1 requests-2.25.1 s3transfer-0.3.4 six-1.15.0 transaction-3.0.1 translationstring-1.4 urllib3-1.26.3 venusian-3.0.0 waitress-1.4.4 webob-1.8.7 zope.deprecation-4.4.0 zope.interface-5.2.0 zope.sqlalchemy-1.3
.venv/bin/pip install -i http://localhost:6543/simple/ 'pypicloud[server]'  7.41s user 0.43s system 30% cpu 25.938 total

サーバーの出力のほう。キャッシュするために、pypicloudがWheelをダウンロードしていっている。
中間キャッシュとして一度ダウンロードしている以上、初回は遅いのは普通の挙動。

INFO 2021-02-23 17:32:07,394 [pypicloud.views.api] Caching python_dateutil-2.8.1-py2.py3-none-any.whl from https://pypi.org/simple
INFO 2021-02-23 17:32:07,783 [pypicloud.views.api] Caching pycparser-2.20-py2.py3-none-any.whl from https://pypi.org/simple
INFO 2021-02-23 17:32:08,339 [pypicloud.views.api] Caching PasteDeploy-2.1.1-py2.py3-none-any.whl from https://pypi.org/simple

After-2

.venvをまっさらにしてもう一回。

$ time .venv/bin/pip install -i http://localhost:6543/simple/ 'pypicloud[server]'
Looking in indexes: http://localhost:6543/simple/
Collecting pypicloud[server]
  Downloading http://localhost:6543/api/package/pypicloud/pypicloud-1.1.7-py2.py3-none-any.whl (537 kB)
     |████████████████████████████████| 537 kB 95.7 MB/s
Successfully installed Jinja2-2.11.3 MarkupSafe-1.1.1 PasteDeploy-2.1.1 SQLAlchemy-1.3.23 beaker-1.11.0 boto3-1.17.13 botocore-1.20.13 certifi-2020.12.5 cffi-1.14.5 chardet-4.0.0 cryptography-3.4.6 distlib-0.3.1 hupper-1.10.2 idna-2.10 jmespath-0.10.0 passlib-1.7.4 paste-3.5.0 plaster-1.0 plaster-pastedeploy-0.7 pycparser-2.20 pypicloud-1.1.7 pyramid-1.10.7 pyramid-beaker-0.8 pyramid-duh-0.1.2 pyramid-jinja2-2.8 pyramid-rpc-0.8 pyramid-tm-2.4 python-dateutil-2.8.1 requests-2.25.1 s3transfer-0.3.4 six-1.15.0 transaction-3.0.1 translationstring-1.4 urllib3-1.26.3 venusian-3.0.0 waitress-1.4.4 webob-1.8.7 zope.deprecation-4.4.0 zope.interface-5.2.0 zope.sqlalchemy-1.3
.venv/bin/pip install -i http://localhost:6543/simple/ 'pypicloud[server]'  4.22s user 0.50s system 85% cpu 5.517 total

pypicloud側でのダウンロードがない分、だいぶ高速になった。

Webインターフェースの様子

中間キャッシュとしてダウンロードしたパッケージが登録されている

脚注
  1. 初回は当然ではある ↩︎

Takei KazuyaTakei Kazuya

自作したパッケージを登録する

主にこのようなケース用。

  • 共通化された業務ロジックをプライベートに公開したい
  • ちょっとしたライブラリを作ったけどPyPIへの登録はちょっと

適当なパッケージを用意

$ poetry init                                                                                                                                                                                                         1

This command will guide you through creating your pyproject.toml config.

Package name [local-pypi-demo]:  demo
Version [0.1.0]:
Description []:
Author [Kazuya Takei <myself@attakei.net>, n to skip]:
License []:
Compatible Python versions [^3.9]:

Would you like to define your main dependencies interactively? (yes/no) [yes] no
Would you like to define your development dependencies interactively? (yes/no) [yes] no
Generated file

Do you confirm generation? (yes/no) [yes] yes
demo.py
def hello():
    return "world"

この2個があれば、poetry buildでパッケージを用意できる。[1]

アップロード先を追加登録して、プッシュする

$ poetry config --local repositories.local "http://0.0.0.0:6543/simple/"
$ poetry config --local http-basic.local username password
No suitable keyring backends were found
Using a plaintext file to store and retrieve credentials
$ poetry config list 
.
repositories.local.url = "http://0.0.0.0:6543/simple/"
.
$ poetry publish -r local

No suitable keyring backends were found
Using a plaintext file to store and retrieve credentials
Publishing demo (0.1.0) to local
 - Uploading demo-0.1.0-py3-none-any.whl 100%
 - Uploading demo-0.1.0.tar.gz 100%

poetry config でpoetry環境に各種設定を追加できる。--localをつけたのは、このプロジェクトに限定するため。

poetry config repositories.XXX だと、 XXXという名前をつけてパッケージリポジトリを新規追加できる。

poetry config http-basic.XXX で、対応するリポジトリにアクセスする際のBasic認証情報を保持できる。

一通り設定が終わればpoetry publish -r XXX で用意したXXXというパッケージリポジトリにパッケージをプッシュする。

確認

他のワークスペースでインストール確認

$ venv/bin/pip install -i http://localhost:6543/simple/ demo
Looking in indexes: http://localhost:6543/simple/
Collecting demo
  Downloading http://localhost:6543/api/package/demo/demo-0.1.0-py3-none-any.whl (966 bytes)
Installing collected packages: demo
Successfully installed demo-0.1.0
WARNING: You are using pip version 20.2.3; however, version 21.0.1 is available.
You should consider upgrading via the '/home/attakei/works/demo/local-pypi-workspace/.venv/bin/python -m pip install --upgrade pip' command.
demo/local-pypi-workspace
Python 3.9.1 (default, Feb  6 2021, 06:49:13)
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import demo
>>>
>>> demo.hello()
'world'
脚注
  1. tar.gzとwheelの2種類 ↩︎

Takei KazuyaTakei Kazuya

pypicloudを認証制にする

「このパッケージは全体公開はしたくないなぁ」というケース用。

最低限必要な設定

config.ini
 pypi.default_read =
-     everyone
+     authenticated

pypicloudのconfig.iniで、pypi.default_readeveryoneからauthenticatedに変えるだけ。

実際の挙動-ブラウザ

ブラウザからでは、UI自体は表示されるがパッケージが出なくなる。

設定済みの認証情報でログインすると、表示されるようになる。

実際の挙動-pip

$ .venv/bin/pip install -i http://localhost:6543/simple/ demo
Looking in indexes: http://localhost:6543/simple/
User for localhost:6543: admin
Password:
Collecting demo
  Downloading http://localhost:6543/api/package/demo/demo-0.1.0-py3-none-any.whl (966 bytes)
Installing collected packages: demo
Successfully installed demo-0.1.0
WARNING: You are using pip version 20.2.3; however, version 21.0.1 is available.
You should consider upgrading via the '/home/attakei/works/demo/local-pypi-workspace/.venv/bin/python -m pip install --upgrade pip' command.

User for localhost:6543:
Password:
という2個の認証プロンプトが出るようになった。設定済みの認証情報を与えることでインストールが出来る。

実際の挙動-poetry

poetryの環境下に依存関係として要認証のpypicloud上のパッケージを引っ張りたい場合。

事前にpypicloud側の設定を少しいじる

config.ini
-pypi.fallback = cache
+pypi.fallback = redirect

pypi.fallbackは、pypicloud上に管理されていないパッケージについてのリクエストが来た場合の振る舞いを指定するのだが、redirectにしておくことで「ここでそれは管理してないからそっち行って」と返せるようになる。cacheだと「取ってくるからちょっと待ってて」となる。

pyproject.tomlにパッケージのソース入手先として、新しくリポジトリを追加してやる必要がある。

pyproject.toml
[[tool.poetry.source]]
name = "local"
url = "http://0.0.0.0:6543/simple/"

この設定を行うと、poetry addを行う際にまずlocalを見に行き、なければPyPIを見に行くようになる

認証情報も忘れない。

$ poetry config --local http-basic.local username password