Pipenv から Poetry への乗り換え
乗り換えるリポジトリ
GitHub - kk6/aeroplast: Transparent PNG conversion (Mainly for Twitter)
twitterにアップロードしたPNGがJPGに強制変換されて見栄えが悪いという問題がある。それを回避するために画像の四隅どこかに1pxの透過ドットを打ち込むという方法がある。いちいちペイントソフトでそれを毎回やるのが面倒なのでコマンドラインで画像のPath渡したら変換してくれるコマンドラインツールを以前作った。こいつをPipenvで管理しているので、Poetryへの乗り換えを試してみる。
Poetry については数日中にもうちょっと突っ込んだ記事書きます。
pyproject.toml の生成
init
コマンドで対話形式で情報を入力していくだけ。
$ poetry init This command will guide you through creating your pyproject.toml config. Package name [aeroplast]: Version [0.1.0]: Description []: Transparent PNG conversion. Author [kk6 <hiro.ashiya@gmail.com>, n to skip]: License []: MIT Compatible Python versions [^3.7]: Would you like to define your dependencies (require) interactively? (yes/no) [yes] no Would you like to define your dev dependencies (require-dev) interactively (yes/no) [yes] no Generated file [tool.poetry] name = "aeroplast" version = "0.1.0" description = "Transparent PNG conversion." authors = ["kk6 <hiro.ashiya@gmail.com>"] license = "MIT" [tool.poetry.dependencies] python = "^3.7" [tool.poetry.dev-dependencies] [build-system] requires = ["poetry>=0.12"] build-backend = "poetry.masonry.api" Do you confirm generation? (yes/no) [yes] yes
これで pyproject.toml が生成される。
依存パッケージのインストール
続いて Pipfile を見ながらパッケージを poetry add
で追加する。
(追記) だるいのでツール作った
(追記おわり)
$ poetry add pillow fire logzero Using version ^5.3 for pillow Using version ^0.1.3 for fire Using version ^1.5 for logzero Updating dependencies Resolving dependencies... (3.2s) Package operations: 0 installs, 1 update, 0 removals Writing lock file - Updating six (1.11.0 -> 1.12.0)
インストールしたパッケージは tool.poetry.dependencies
セクションに追記されている。
[tool.poetry] name = "aeroplast" version = "0.1.0" description = "Transparent PNG conversion." authors = ["kk6 <hiro.ashiya@gmail.com>"] license = "MIT" [tool.poetry.dependencies] python = "^3.7" pillow = "^5.3" fire = "^0.1.3" logzero = "^1.5" [tool.poetry.dev-dependencies] [build-system] requires = ["poetry>=0.12"] build-backend = "poetry.masonry.api"
と同時に poetry.lock
ファイルが生成されている。依存関係はこちらに記述されている。
pytestのインストールを忘れていた。
$ poetry add -D pytest Using version ^4.0 for pytest Updating dependencies Resolving dependencies... (0.5s) Package operations: 0 installs, 1 update, 0 removals Writing lock file - Updating pytest (4.0.1 -> 4.0.2)
今回は -D
オプションを付けてインストールしたので、pyproject.toml の tool.poetry.dev-dependencies
セクションの方に追記されている。
[tool.poetry.dependencies] python = "^3.7" pillow = "^5.3" fire = "^0.1.3" logzero = "^1.5" [tool.poetry.dev-dependencies] pytest = "^4.0"
コマンドラインツールとして実行できるようにする
PipenvのときはPipfileに以下のように記述して使っていた。
[scripts] cli = "pipenv run python -m aeroplast"
こんな感じで使っていた
$ pipenv run cli convert ...
Potryの場合は pyproject.toml
の tool.poetry.scripts
セクションに記述する。
[tool.poetry.scripts] aeroplast = "aeroplast.cli:main"
Poetryの便利なところは、パッケージをビルドした際にこの設定がそのまま setup.py
の console_scripts
として記述される。ビルドする時にコマンドとして実行できるようにあらためてsetup.pyを書く必要がない。
... entry_points = \ {'console_scripts': ['aeroplast = aeroplast.cli:main']} ...
とまあそれはさておき poetry run aeroplast
で実行できるようになった。
$ poetry run aeroplast Type: CLI String form: <aeroplast.cli.CLI object at 0x100f6e978> Docstring: Transparent PNG conversion Usage: aeroplast aeroplast convert aeroplast resize
また、poetry develop
で pip install -e .
するのと同様に開発途中のパッケージをvenvにインストールできる。インストールした上で poetry shell
でvenvをactivateすると aeroplast
コマンドのみで実行可能になる。
$ poetry shell Spawning shell within /Users/kk6/PycharmProjects/aeroplast/.venv Welcome to fish, the friendly interactive shell $ poetry develop develop is deprecated use install instead. Installing dependencies from lock file Nothing to install or update - Installing aeroplast (0.1.0) $ aeroplast Type: CLI String form: <aeroplast.cli.CLI object at 0x107dfa908> Docstring: Transparent PNG conversion Usage: aeroplast aeroplast convert aeroplast resize <aeroplast.cli.CLI object at 0x107dfa908>
以上で移行完了。わりと簡単。しかも今すぐにでも以下の2行を実行するだけでPyPIに公開可能な状態となっている。
$ poetry build $ poetry publish
Poetry便利。
またPyenv環境に戻してみた
- 動機
- 事前確認
- pyenvのインストール
- python3.6をインストールしてみるも失敗する
- 最近Mojaveにアップグレードしたのが原因だった
- それぞれのPythonを呼び出せるようにする
- brewで入れたpython3.7をどうするか問題
- brewのpython消してpyenvで入れ直した
動機
ライブラリ開発とかしてるとどうしてもhomebrewだけではつらくなる(python3.6が入ってる状態でbrew upgradeしてpython3.7に上がると、brew cleanup叩いた時点でpython3.6が消される)
公式からバージョンごとにインストーラー落としてきて入れるというのも考えたが、pipenvにはpyenvと連携するオプションが用意されてるし、CircleCIも内部でpyenv使ってるしpoetryの作者もとあるissueに対する返信で「pyenvを使うことを強く推奨する」なんて言ってたのでもう海外でもpyenvが市民権得たのかなーとか思った次第。じゃあもうpyenvでいいやーと。
事前確認
- 2.7.10
- 3.7.0
pyenvのインストール
~ $ brew install pyenv ==> Installing dependencies for pyenv: autoconf ==> Installing pyenv dependency: autoconf ==> Downloading https://homebrew.bintray.com/bottles/autoconf-2.69.mojave.bottle.4.tar.gz ######################################################################## 100.0% ==> Pouring autoconf-2.69.mojave.bottle.4.tar.gz ==> Caveats Emacs Lisp files have been installed to: /usr/local/share/emacs/site-lisp/autoconf ==> Summary 🍺 /usr/local/Cellar/autoconf/2.69: 71 files, 3.0MB ==> Installing pyenv ==> Downloading https://homebrew.bintray.com/bottles/pyenv-1.2.8.mojave.bottle.tar.gz ######################################################################## 100.0% ==> Pouring pyenv-1.2.8.mojave.bottle.tar.gz 🍺 /usr/local/Cellar/pyenv/1.2.8: 612 files, 2.4MB ==> Caveats ==> autoconf Emacs Lisp files have been installed to: /usr/local/share/emacs/site-lisp/autoconf
pyenvを使えるようにするために、bash等の場合は.bash_profile等にパスの追記等が必要だが、自分はfish shell & fisherman を使っているので、このへんはプラグインを入れるだけでOK.
~ $ fisher pyenv Installing 1 plugin/s OK Fetch pyenv github.com/fisherman/pyenv Done in 2s 26ms
python3.6をインストールしてみるも失敗する
~ $ pyenv install 3.6.7 python-build: use openssl from homebrew python-build: use readline from homebrew Downloading Python-3.6.7.tar.xz... -> https://www.python.org/ftp/python/3.6.7/Python-3.6.7.tar.xz Installing Python-3.6.7... python-build: use readline from homebrew BUILD FAILED (OS X 10.14 using python-build 20180424) Inspect or clean up the working tree at /var/folders/kb/knxvk74s36s20fd1n2sh7g200000gq/T/python-build.20181107053928.7189 Results logged to /var/folders/kb/knxvk74s36s20fd1n2sh7g200000gq/T/python-build.20181107053928.7189.log Last 10 log lines: File "/private/var/folders/kb/knxvk74s36s20fd1n2sh7g200000gq/T/python-build.20181107053928.7189/Python-3.6.7/Lib/ensurepip/__main__.py", line 5, in <module> sys.exit(ensurepip._main()) File "/private/var/folders/kb/knxvk74s36s20fd1n2sh7g200000gq/T/python-build.20181107053928.7189/Python-3.6.7/Lib/ensurepip/__init__.py", line 204, in _main default_pip=args.default_pip, File "/private/var/folders/kb/knxvk74s36s20fd1n2sh7g200000gq/T/python-build.20181107053928.7189/Python-3.6.7/Lib/ensurepip/__init__.py", line 117, in _bootstrap return _run_pip(args + [p[0] for p in _PROJECTS], additional_paths) File "/private/var/folders/kb/knxvk74s36s20fd1n2sh7g200000gq/T/python-build.20181107053928.7189/Python-3.6.7/Lib/ensurepip/__init__.py", line 27, in _run_pip import pip._internal zipimport.ZipImportError: can't decompress data; zlib not available make: *** [install] Error 1
最近Mojaveにアップグレードしたのが原因だった
mac os Mojave にアップグレードしたら bundle install が失敗する事象を解決した - Qiita
~ $ open /Library/Developer/CommandLineTools/Packages/ The file /Library/Developer/CommandLineTools/Packages does not exist.
上記リンクのQiita記事を参考にopen
してみるも、無いって言われる。記事の一番下の参照元リンク先みるとxcode-select
が必要っぽい。
~ $ xcode-select --install xcode-select: note: install requested for command line developer tools
インストール完了後、再びopen
コマンド実行すると今度はちゃんとフォルダが開くので、macOS_SDK_headers_for_macOS_10.14.pkg
をダブルクリックでインストールする。
再びpyenvでpython3.6をインストールすると、今度は無事成功。
それぞれのPythonを呼び出せるようにする
simlink作成(非推奨)
tox等で使えるよう、python3.6
コマンドで呼び出せるようにしたい。何も考えずに最初はsimlinkで対応した。
$ cd /usr/local/bin $ ln -s ~/.pyenv/versions/3.6.7/bin/python3.6 .
でも後で気づいたが公式のやりかたがあった
pyenv global コマンド
~ $ pyenv versions * system (set by /Users/kk6/.pyenv/version) 2.7.15 3.4.9 3.5.6 3.6.7
上記のような状態で、とりあえず3系全部動くようにしたいとする。pyenv global
に使いたいバージョンを列挙すればいいだけ。
~ $ pyenv global 3.4.9 3.5.6 3.6.7 ~ $ pyenv versions system 2.7.15 * 3.4.9 (set by /Users/kk6/.pyenv/version) * 3.5.6 (set by /Users/kk6/.pyenv/version) * 3.6.7 (set by /Users/kk6/.pyenv/version) ~ $ python3.5 Python 3.5.6 (default, Nov 7 2018, 06:07:53) [GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.11.45.5)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>>
これだけ。いちいちsimlink張るより楽。ただ、simlinkのほうが起動は速い。
【追記】
pyenv global
するとき、バージョンが新しいものから列挙してやらないとpython3
コマンドが3.4
を見に行ったりするので以下のようにする。
$ pyenv global 3.7.1 3.6.7 3.5.6 3.4.9 2.7.15
brewで入れたpython3.7をどうするか問題
brewのpythonとpyenvのpython混在とか地獄を見そうなのでbrewのpythonを消そうと思ったが…
~ $ brew uninstall python3 Error: Refusing to uninstall /usr/local/Cellar/python/3.7.0 because it is required by thefuck and vim, which are currently installed. You can override this and force removal with: brew uninstall --ignore-dependencies python3
python3に依存してるやつらがいるから消せないとのこと。
thefuck
に関してはわりとどうでもいい。vim
もいい機会だしneovim
にするのもありかもしれない。pyenvのpython3.7インストールしたら問題無さそうな気もするし、問題があればpythonインストール後にこいつらをbrewでインストールし直すのでもいけそうではある。
brewのpython消してpyenvで入れ直した
やっぱり3.7だけbrewで他のバージョンがpyenvなの気持ち悪すぎるのでpyenvに統一する。
$ brew uninstall thefuck vim python3
したあと$ pyenv install 3.7.1
した。
で、既存のvenvが壊れるのは想定内で、pipenvが壊れるのも想定内(brewのpython3.7で入れていた)。ただ、pipも動かないとは思ってなかった。
$ pipenv Failed to execute process '/Users/kk6/.local/bin/pipenv'. Reason: The file '/Users/kk6/.local/bin/pipenv' specified the interpreter '/usr/local/opt/python/bin/python3.7', which is not an executable command. $ pip Failed to execute process '/usr/local/bin/pip'. Reason: The file '/usr/local/bin/pip' specified the interpreter '/usr/local/opt/python/bin/python3.7', which is not an executable command. $ which pip /usr/local/bin/pip
エラーメッセージ見れば分かる通り、/usr/local/bin/
にゴミが残っている。ついでにpip3も消しておく。
$ which pip /Users/kk6/.pyenv/shims/pip $ pip Usage: pip <command> [options] Commands: install Install packages. download Download packages. ...
これで動くようになった。
あとはpipenvを入れ直す。
$ pip install --user pipenv
以上でひとまずpyenvへの移行完了です。
RSCSS という CSS 設計について
(追記)
実践例あった
http://ricostacruz.com/rscss/index.html
追記終わり。
https://github.com/rstacruz/rscss
しばらく CSS とか追ってなかったので、触るにあたって「むやみにCSS書いてたら後で確実に死ぬし、そういえばなんかOOCSSとかあったな」と思っていろいろ調べてたら OOCSS の他にも SMACSS とか BEM とか SuitCSS とか FLOCSS とかなんかいろいろ出てきて大変でした。たしか SMACSS くらいまでは記憶があるんだけど…。
で、どうもどれもしっくり来ないのでさらに調べてみると RSCSS というものを発見。「フレームワークじゃなくてあくまでもアイデア集だよ」ってことらしく、ルールガチガチでもないしなんだかよさ気。
で、RSCSS の README を読みながらメモしたんでせっかくだし公開します。ほぼただ訳しただけなんですが…。英語あまり得意ではないので間違いなど指摘いただけると助かります。
Components
検索フォームのようなひとかたまりをひとつのコンポーネントとして考える
Components の命名規則
Components は ダッシュで区切られた 少なくとも2つの単語 からなる。
- Like ボタン (
.like-button
) - 検索フォーム (
.search-form
) - ニュースカード (
.article-card
)
Elements
Elements とは Component を構成する内部要素。
Elements の命名規則
それぞれの Component は通常、複数の Element を持つ。 Element の名前は 1単語 にする。
.search-form { > .field { /* ... */ } > .action { /* ... */ } }
Element のセレクター
可能な限り子セレクタ >
を使う。Componentのネスト防止になるし、子孫セレクタよりパフォーマンスがいい。
.article-card { .title { /* これでもいいけど */ } > .author { /* ✓ こっちのほうがいい */ } }
複数の単語が使いたい
2つ以上の単語からなる名前をつけたい場合は、ダッシュやアンダースコアを使わないで単純に連結する。
.profile-box { > .firstname { /* ... */ } > .lastname { /* ... */ } > .avatar { /* ... */ } }
タグセレクタを避ける
可能な限りクラス名を使用する。タグセレクタはよいものだが、若干のパフォーマンス上のペナルティがあり、また説明的ではないかもしれない。
.article-card { > h3 { /* ✗ これは避ける */ } > .name { /* ✓ こうするほうがいい */ } }
Variant
Component も Element もそれぞれ variant を持つことができる。
Variant の命名
variant のためのクラス名にはダッシュ ( -
) を prefix としてつける
.like-button { &.-wide { /* ... */ } &.-short { /* ... */ } &.-disabled { /* ... */ } }
Element の variant
Element もまた variant をもつことができる
.shopping-card { > .title { /* ... */ } > .title.-small { /* ... */ } }
ダッシュ・プレフィックス
ダッシュは variant のプレフィックス(接頭辞)として使用できる
- Element との曖昧さ防止
- CSSのクラス名はアルファベットそして
_
または-
で始めることができる - ダッシュはアンダースコアよりタイプしやすい
- it kind of resembles switches in UNIX commands (
gcc -O2 -Wall -emit-last
)
(ダッシュやアンダースコアではじまるクラス名ってOKなの知らなかった)
In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code (see next item). For instance, the identifier "B&W?" may be written as "B&W\?" or "B\26 W\3F".
http://www.w3.org/TR/CSS21/syndata.html#characters
ネストした Component
<div class='article-link'> <div class='vote-box'> ... </div> <h3 class='title'>...</h3> <p class='meta'>...</p> </div>
時には Component をネストする必要がでてくる
ネストした Component の Variant
Component は何らかの方法で他のComponentにネストする必要が生じるかもしれない。ネストしたコンポーネントを、それを内包するコンポーネントに突っ込むような変更は避ける。
.article-header { > .vote-box > .up { /* ✗ これは避ける */ } }
かわりに、ネストした Component に Variant を追加し、それを含む component からそれを適用するのが好ましい。
<div class='article-header'> <div class='vote-box -highlight'> ... </div> ... </div>
.vote-box { &.-highlight > .up { /* ... */ } }
ネストした Component の単純化
<div class='search-form'> <input class='input' type='text'> <button class='search-button -red -large'></button> </div>
CSS のプリプロセッサーの @extend
によって単純化することができる
<div class='search-form'> <input class='input' type='text'> <button class='submit'></button> </div>
.search-form { > .submit { @extend .search-button; @extend .search-button.-red; @extend .search-button.-large; } }
Layout
ポジションに関するプロパティを避ける
Component は異なるコンテキストで再利用できるべきなので、以下のようなプロパティを書くのを避ける
- Positioning (position, top, left, right, bottom)
- Floats (float, clear)
- Margins (margin)
- Dimensions (width, height) *
固定寸法
アバターやロゴのような width/height が固定される要素については例外とする
親要素でポジションを定義する
これらを定義したいなら、それらが所属するコンテキストに定義する。下記の例で言うと、 list コンポーネントに適用するのであって、 .article-card 自身に定義するのではない。
.article-list { & { @include clearfix; } > .article-card { width: 33.3%; float: left; } } .article-card { & { /* ... */ } > .image { /* ... */ } > .title { /* ... */ } > .category { /* ... */ } }
Helpers
._unmargin { margin: 0 !important; } ._center { text-align: center !important; } ._pull-left { float: left !important; } ._pull-right { float: right !important; }
汎用クラスはアンダースコアで始まる名前で別ファイルに置かれ、値を上書きする。通常、 !important
でタグ付けされる。慎重に使うこと。
Helper の命名
アンダースコアをクラス名のプレフィックスとして付ける。これは component に定義された modifier と容易に見分けることができる。アンダースコアはちょっと不格好で内部的な副作用がある。Helperの使い過ぎには嫌気が差すだろう。
<div class='order-graphs -slim _unmargin'> </div>
Helper の整理
helpers というひとつのファイルに収める。複数ファイルに分割することもできるが、たくさんの helper を最小限に維持するのが好ましい。
CSS structure
ファイル単位でひとつの Component
それぞれの Component はそれぞれひとつのファイルに収める
/* css/components/search-form.scss */ .search-form { > .button { /* ... */ } > .field { /* ... */ } > .label { /* ... */ } // variants &.-small { /* ... */ } &.-wide { /* ... */ } }
Use glob matching
@import 'components/*';
過剰なネストを避ける
できるだけ1階層のネストにとどめる。ネストし過ぎは簡単に迷子になる。
/* ✗ Avoid: 3 levels of nesting */ .image-frame { > .description { /* ... */ > .icon { /* ... */ } } } /* ✓ Better: 2 levels */ .image-frame { > .description { /* ... */ } > .description > .icon { /* ... */ } }
落とし穴
ネストした Component で死亡
ネストした Component が似たような名前の Element を持つ場合に気をつける。
<article class='article-link'> <div class='vote-box'> <button class='up'></button> <button class='down'></button> <span class='count'>4</span> </div> <h3 class='title'>Article title</h3> <p class='count'>3 votes</p> </article>
.article-link { > .title { /* ... */ } > .count { /* ... (!!!) */ } } .vote-box { > .up { /* ... */ } > .down { /* ... */ } > .count { /* ... */ } }
この場合、 .article-link > .count
は >
セレクタを持っておらず、おそらく .vote-box .count
に適用される。これは子セレクタが好ましい理由のひとつ。
懸念事項
ダッシュとか最悪
: このルールを無視して普通の単語をつかってもいい。ただ、 Component - Element - Variant という考えは忘れないで欲しい2単語なんて思いつかない
: alert のような、いくつかの Component は表現上の理由から1単語であることを要する。これらはサフィックス(接尾辞)を使用することでブロック要素だということをより明確にすることを検討したほうがよい。.alert-box
.alert-card
.alert-block
Or for inlines:
.link-button
.link-span
その他のリソース
その他のソリューション
BEM
BEM は素晴らしいけどシンタックスがちょっとキモいという人もいると思う。RSCSSはBEMとはシンタックスが違うだけで、慣習にはほぼ従っている。
<!-- BEM --> <form class='site-search site-search--full'> <input class='site-search__field' type='text'> <button class='site-search__button'></button> </form>
<!-- rscss --> <form class='site-search -full'> <input class='field' type='text'> <button class='button'></button> </form>
用語
同様のコンセプトはその他のCSS設計にもみられる
RSCSS | BEM | SMACSS |
---|---|---|
Component | Block | Module |
Element | Element | Sub-Component |
Layout | ? | Layout |
Variant | Modifier | Sub-Module & State |