やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

twineでアップロードに四苦八苦した結果、超絶に難しいことが発覚した失敗ログ

 もうめちゃくちゃ。

twineでPyPIへアップロードしようとするがエラー

twine upload --repository testpypi dist/*
InvalidConfiguration: Missing 'testpypi' section from the configuration file
or not a complete URL in --repository-url.
Maybe you have a out-dated '~/.pypirc' format?
more info: https://docs.python.org/distutils/packageindex.html#pypirc

~/.pypircファイルは存在している

$ find $HOME/.pypirc
/home/pi/.pypirc
$ cat $HOME/.pypirc
[distutils]
index-servers =
  pypi
  pypitest

[pypi]
repository: https://upload.pypi.org/legacy/
username: XXXXXXXXXXXXX
password: XXXXXXXXXXXXX

[pypitest]
repository: https://test.pypi.org/legacy/
username: XXXXXXXXXXXXX
password: XXXXXXXXXXXXX

コマンドで~/.pypircを指定する

twine upload --config-file "$HOME/.pypirc" --repository testpypi dist/*
InvalidConfiguration: Missing 'testpypi' section from the configuration file
or not a complete URL in --repository-url.
Maybe you have a out-dated '~/.pypirc' format?
more info: https://docs.python.org/distutils/packageindex.html#pypirc

 同様のエラー。

ググった

 他にも困っている人たちがいるっぽい。環境によってエラーにならない人もいるのか?

 結局、解決していないってことなの? よくわからん。

~/.pypircは使えない……

 やり方を変えてみた。~/.pypircを使わず、ユーザ名・パスワード・URLをすべてコマンドで直接指定した。

UN=ユーザ名
PW=パスワード
twine upload -u $UN -p $PW --repository-url https://test.pypi.org/legacy/ dist/*
Uploading distributions to https://test.pypi.org/legacy/
Uploading mypack-0.0.2-py2-none-any.whl
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████| 10.8k/10.8k [00:01<00:00, 8.33kB/s]
NOTE: Try --verbose to see response content.
HTTPError: 403 Client Error: The credential associated with user 'ytyaru' isn't allowed to upload to project 'mypack'. See https://test.pypi.org/help/#project-name for more information. for url: https://test.pypi.org/legacy/

 進んだ。つまり、~/.pypircは無効化されて使えない。なんでや……。とりあえず放置。

名前の既存エラー

 別の新たなエラーが出ていた。これを翻訳すると以下。

注:応答コンテンツを表示するには、-verboseを試してください。
HTTPError:403クライアントエラー:ユーザー 'ytyaru'に関連付けられた資格情報は、プロジェクト 'mypack'へのアップロードを許可されていません。 詳細については、https://test.pypi.org/help/#project-nameを参照してください。 URLの場合:https://test.pypi.org/legacy/

 名前が既存である旨のエラーらしい。以下で調べてみると、たしかに既存だった。

パッケージの名前を変える

 アップロード手順コマンド。

rm -rf build
rm -rf dist
rm -rf mypack.egg-info

python setup.py sdist
python setup.py bdist_wheel

push(自作スクリプト。git add, git commit, git pushを一発でやる)

version='v0.0.3'
message="Package name changed from 'mypack' to 'mypack_ytyaru_20200112'"
git tag -a "$version" -m "$message"
git push origin "$version"

UN={USERNAME}
PW={PASSWORD}
twine upload -u $UN -p $PW --repository-url https://test.pypi.org/legacy/ dist/*

 この後、細かい色々なミスによりタグの削除が必要になった。以下の手順。

git tag -d v0.0.3
git push origin :refs/tags/v0.0.3
git tag v0.0.3
git push --tags origin

 そしてタグの削除さえミスった。しかもそれは修復不可能だった……。

 さらに、PyPIでは公開したバージョンより新しい値でないと二度とアップロードできない。よって、適当にsetup.pyのバージョン値だけあげてpushしたコードをPyPIに公開することになった。

 もうめちゃくちゃ……。リビジョンの整合性が壊れた。

 というか、公開手順でチェックすべきことがあまりに多すぎる。絶対にミスする。あとでまとめるべき。

今回陥った負のスパイラル

  • 1-1. 公開手順でやることが多すぎて忘れる
  • 1-2. どれかひとつを忘れてバージョン更新処理する
  • 1-3. 上記を無限ループして進まない

 無理やり進めた結果、以下のように悪化。

  • 2-1. 1-2で不正内容をリリースしてしまう
  • 2-2. 上記の状態を修正すべくタグを削除&再作成で作り直そうとする
  • 2-3. タグを間違って削除してリビジョンの整合性が破綻する

 そして無情にも以下のような縛りがある。

  • 一度PyPIにアップロードしたら二度と削除できない

 やり直しが効かない一発勝負だった。これはひどい……。後始末すらできないのかよ。すぐゴミ屋敷になるぞ?

 仕方ないのでリリースとコミットの整合性が破綻したまま、バージョン値だけインクリメントして適当にコミットすることでPyPIにアップロードできるようにする。

 コード修正ではなく、アップロードするためのコード修正がリビジョン履歴やバージョン値に反映されてしまう……。そのせいで無駄にバージョン値が上がっていく。あのさぁ……。

今回やらかしたミス

  • パッケージ名がPyPIで既存だった
  • __init__.pysetup.pyメタデータを個別に書いていたので共通化した
  • README.md内のリンク[ja](./ReadMe.ja.md)[ja](./README.ja.md)に修正
  • バージョン値をインクリメントし忘れた
  • CHANGES.mdに追記し忘れた
  • git tagを間違って削除してしまった(適当に__init__.py__version__をインクリメントしただけでpushして最新PyPIバージョン値にまで繰り上げた)
  • pythonパッケージ, pythonモジュール, README.md, LICENSE.txtなどのファイル名とその内容と、__init__.py,setup.pyの各変数が不一致
    • これらを自動的に一致させないと必ずミスる!
    • たとえばsetup.pynamepythonパッケージ名、pythonモジュール名が不一致だった(見逃すとpipでインストール時に実行時エラーになるまで気づけないと思われる)
  • CHANGES.mdと__init__.py__version__git commit -mのコミットメッセージとgit tag -a -mのタグ名とメッセージの不一致または記載忘れ
    • これらを自動的に一致させないと必ずミスる!
    • 面倒になってCHANGES.mdのメンテしなくなる未来しか見えない

 やることが多すぎて整理しきれない……。整理したところで自動化しないと絶対に遂行できない。

アップロードに成功

 アップロードに成功したら以下のようなログになる。

twine upload -u $UN -p $PW --repository-url https://test.pypi.org/legacy/ dist/*
Uploading distributions to https://test.pypi.org/legacy/
Uploading mypack_ytyaru_20200112-0.0.3-py2-none-any.whl
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████| 11.2k/11.2k [00:02<00:00, 4.02kB/s]
Uploading mypack_ytyaru_20200112-0.0.3.tar.gz
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████| 10.8k/10.8k [00:01<00:00, 5.61kB/s]

View at:
https://test.pypi.org/project/mypack-ytyaru-20200112/0.0.3/
https://test.pypi.org/project/mypack-ytyaru-20200112/

pipでインストールしてみる

 これをインストールするには以下のコマンドらしい。

pip install -i https://test.pypi.org/simple/ mypack-ytyaru-20200112

 だがエラーになった。

Looking in indexes: https://test.pypi.org/simple/, https://www.piwheels.org/simple
Collecting mypack-ytyaru-20200112
  Downloading https://test-files.pythonhosted.org/packages/6a/c8/adff5ff515e3bacd8552cedefca10b9cae19a9bd21595206190627a321cc/mypack_ytyaru_20200112-0.0.4.tar.gz
Building wheels for collected packages: mypack-ytyaru-20200112
  Building wheel for mypack-ytyaru-20200112 (setup.py) ... done
  Created wheel for mypack-ytyaru-20200112: filename=mypack_ytyaru_20200112-0.0.4-cp37-none-any.whl size=6434 sha256=341624a994be6d617e4bd9a49a81ebfbc8d4b993d932fa6f293df1e346ead207
  Stored in directory: /home/pi/.cache/pip/wheels/c2/54/c8/1a4f8d96594478e51178743ce4998feeee8ec2ea53b0d81f97
Successfully built mypack-ytyaru-20200112
Installing collected packages: mypack-ytyaru-20200112
ERROR: Could not install packages due to an EnvironmentError: [Errno 13] 許可がありません: '/usr/local/lib/python3.7/dist-packages/mypack_ytyaru_20200112-0.0.4.dist-info'
Consider using the `--user` option or check the permissions.

 --userオプションをつけると成功。

pip install --user -i https://test.pypi.org/simple/ mypack-ytyaru-20200112
Looking in indexes: https://test.pypi.org/simple/, https://www.piwheels.org/simple
Processing /home/pi/.cache/pip/wheels/c2/54/c8/1a4f8d96594478e51178743ce4998feeee8ec2ea53b0d81f97/mypack_ytyaru_20200112-0.0.4-cp37-none-any.whl
Installing collected packages: mypack-ytyaru-20200112
Successfully installed mypack-ytyaru-20200112-0.0.4

 なんかいつものコマンドと全然ちがうんですけど。こんなクソ長いコマンド絶対打てないって。暗号かよ……。もういいや。またしても一旦放置。

pip存在確認。

pip list | grep mypack
mypack-ytyaru-20200112 0.0.4      

 え? 名前変わってるんですけど……。-でなく_のはずだが? Pythonってスネークケースしか使えないんじゃなかったっけ? なんでチェインケースに変えられちゃったの? やめて?

 もういいやそれで(投げやり)。

pythonimportしてみる

 以下のようにインポートし、メソッドを呼び出すコードを書く。

a1.py

import mypack-ytyaru-20200112
print(mypack-ytyaru-20200112.mymethod())

 実行。

python a1.py

 エラー……。

  File "a1.py", line 1
    import mypack-ytyaru-20200112
                 ^
SyntaxError: invalid syntax

 はぁ? -のところで無効な構文って……勝手に変えられたんだよチクショウが! 私は罠になると思って_にしたのに!!!

 ふ ざ け る な ! クソがーーーーーー!

 勝手に改ざんされるなら、こっちが何をしても無駄じゃん……。

 一応、当初想定していた通り_でも確認してみる。

a2.py

import mypack_ytyaru_20200112
print(mypack_ytyaru_20200112.mymethod())
python a1.py
Traceback (most recent call last):
  File "a2.py", line 1, in <module>
    import mypack_ytyaru_20200112
ImportError: No module named mypack_ytyaru_20200112

 そんなモジュールは無い。ですよねー。

 ……。

 PEP8には非推奨とある。

Python のパッケージ名は、全て小文字の短い名前を使うべきですが、アンダースコアを使うのは推奨されません。

 それに従うと超絶にわかりにくい文字の羅列になる。

 あと、名前が改ざんされて構文エラーになるなんて聞いてない。PyPIの問題だとは思うが。

 それでも、どうせただの命名規則だし、可読性の問題にすぎないと思っていたのに……。非推奨な_を使ったら構文エラーって、心の狭い現れだわ。マジクソだわ。

 たぶん私が何か間違っているか、さもなくばPythonPyPIの仕様なのだろう……。そんな情報は見つけられなかったが。

ハイフン付きのパッケージは存在するのだが?

 たとえば以下のような。

pip list | grep -
distro-info            0.21       
importlib-metadata     1.4.0      
lazy-object-proxy      1.3.1      
logilab-common         1.4.2      
more-itertools         8.0.2      
mypack-ytyaru-20200112 0.0.4      
mypy-extensions        0.4.1      
python-apt             1.8.4      
ranger-fm              1.9.2      
readme-renderer        24.0       
requests-oauthlib      1.0.0      
requests-toolbelt      0.9.1      
sense-hat              2.2.0      
ssh-import-id          5.7        
typed-ast              1.3.1      
unattended-upgrades    0.1      

 こいつらもインポートできないの?

pip show mypack-ytyaru-20200112
Name: mypack-ytyaru-20200112
Version: 0.0.4
Summary: Publish the package with PyPI.
Home-page: https://github.com/ytyaru/Python.PyPI.mypack
Author: ytyaru
Author-email: pypi1@outlook.jp
License: UNKNOWN
Location: /home/pi/.local/lib/python3.7/site-packages
Requires: 
Required-by: 

 モジュールのファイルパスは以下。

/home/pi/.local/lib/python3.7/site-packages/mypack_ytyaru_20200112/mypack_ytyaru_20200112.py
$HOME/.local/lib/python3.7/site-packages/mypack_ytyaru_20200112/mypack_ytyaru_20200112.py
~/.local/lib/python3.7/site-packages/mypack_ytyaru_20200112/mypack_ytyaru_20200112.py

 こっちは作ったとおり_。なんなの?

 PythonのREPRで試しに実行してみる。

python
>>> execfile('/home/pi/.local/lib/python3.7/site-packages/mypack_ytyaru_20200112/mypack_ytyaru_20200112.py')
>>> mymethod()
'mymethod return.'

 できた。でもこれはPython2でのみ使える。python3でやるとエラー。

$ python3
Python 3.7.3 (default, Apr  3 2019, 05:39:12) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> execfile('/home/pi/.local/lib/python3.7/site-packages/mypack_ytyaru_20200112/mypack_ytyaru_20200112.py')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'execfile' is not defined

 python3は以下でやるんだってさ。あのさぁ、どこまで欠陥言語だよ。もううんざり。

import importlib  
foobar = importlib.import_module("mypack-ytyaru-20200112")

 動作確認するにはpip3でまた別途インストールしないといけないから面倒。未確認のまま放置。

 その前にアンインストールできるか確認すべき。もう何も信じられないから。

pip uninstallでアンインストール

pip uninstall mypack-ytyaru-20200112
Uninstalling mypack-ytyaru-20200112-0.0.4:
  Would remove:
    /home/pi/.local/lib/python3.7/site-packages/mypack_ytyaru_20200112-0.0.4.dist-info/*
    /home/pi/.local/lib/python3.7/site-packages/mypack_ytyaru_20200112/*
    /home/pi/.local/lib/python3.7/site-packages/tests/*
Proceed (y/n)? y
  Successfully uninstalled mypack-ytyaru-20200112-0.0.4

 できた。pip listでも見つからない。OK。

$ pip list | grep mypack
$ 

再インストール

 そもそも上記サイトにあったコマンドをコピーした時点ですでに_でなく-だった。以下のように。

pip install -i https://test.pypi.org/simple/ mypack-ytyaru-20200112

 さらに--userフラグを付与せねばインストールできなかったので以下になった。

pip install --user -i https://test.pypi.org/simple/ mypack-ytyaru-20200112

 ここで名前を-から_に戻したらどうなるか?

pip install --user -i https://test.pypi.org/simple/ mypack_ytyaru_20200112
Looking in indexes: https://test.pypi.org/simple/, https://www.piwheels.org/simple
Processing /home/pi/.cache/pip/wheels/c2/54/c8/1a4f8d96594478e51178743ce4998feeee8ec2ea53b0d81f97/mypack_ytyaru_20200112-0.0.4-cp37-none-any.whl
Installing collected packages: mypack-ytyaru-20200112
Successfully installed mypack-ytyaru-20200112-0.0.4\

 _-に改ざんされてしまう。setup.pyでも_にしているんだけどなぁ……。

$ pip list | grep mypack
mypack-ytyaru-20200112 0.0.4  

名前改ざん

 PyPIパッケージ名の_-になってしまう。

 このせいでimportできず使えない。

 名前に関係するのは以下のはず。それらはすべて_にしている。なのにPyPI上では-になってしまう。

  • setup.pyname
  • __init__.pyimport__all__
  • パッケージ名(ディレクトリ名)
  • モジュール名(.pyファイル名)

 原因不明。PythonPyPIの仕様なのだろうか?

所感

 Pythonは触るたび嫌いになる。

 問題ありすぎてまとめることすら憂鬱。どれだけの作業量になることやら……。こんなこともうやりたくないのだが、pipでインストールできなかったら使えないし……。

 なんでこんなに罠ばかりなの? いじめなの? 情弱をふるいにかけてるの? pipに登録できる俺達TUEEEEEみたいな? 流石兄弟的なの?

 こういう暗黙の常識みたいな罠が多すぎる。心が狭い。

 多数の罠や苦難を受け止める心の広さがない人にPythonを使うのは難しい。

 Pythonは時間と手間暇が奪われることを常識とせねば付き合えない。

対象環境

$ uname -a
Linux raspberrypi 4.19.75-v7l+ #1270 SMP Tue Sep 24 18:51:41 BST 2019 armv7l GNU/Linux