Hatena::ブログ(Diary)

Memo

2017-10-19

[] Misspell を Vim から呼べるようにした

GitHub - heavenshell/vim-misspell: misspell for Vim.

Misspell が便利だったので Vim から呼べるようにプラグイン化した*1


直接 buffer を misspell に突っ込んだら Vim が SEGV したので channel 経由で入れたらうまくいった*2

便利。

*1:多分 ale にはないはず

*2:原因を追求する気力がない

2017-07-17

[] MacPorts + MySQL5.7

プライベートな環境を久々に更新しようと思ってやったら思いの外はまった。

仕事は homebrew だけどプライベートマシンは MacPorts を使ってる。

*1


Python3.6.0, MySQL5.6 だったので Python3.6.1, MySQL5.7.17 にするかとports upgrade outdated して、MySQL を新規にインストールしようとしたらどハマりしたのでメモ。


入れる先。

  • /opt/local/var/run/mysql57
    • chown _mysql:_mysql /opt/local/var/run/mysql57
  • /opt/local/var/db/mysql57
    • chown _mysql:_mysql /opt/local/var/db/mysql57
  • /opt/local/var/log/mysql57
    • chown _mysql:_mysql /opt/local/var/log/mysql57

`$ sudo -u _mysql /opt/local/lib/mysql57/bin/mysqld --initialize` をするとに権限がないと怒られるので、`--tmpdir=/tmp` をつける。

`$ sudo /opt/local/lib/mysql57/bin/mysqld --initialize --user=_mysql --tmpdir=/tmp`


これでログに色々非推奨になったものとかが出てくるので、まずは my.cnf をきれいにする。

 innodb_large_prefix
-innodb_file_format=Barracuda
-sql_mode="NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES"
+sql_mode="NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER"
+explicit_defaults_for_timestamp=1

これで非推奨な警告らしいものは消えた。


起動したはずだからログに出てるパスワードを使って cli から接続してみると、

Error: Can't connect to local MySQL server through socket '/opt/local/var/run/mysql57/mysqld.sock' (2)

が出た。


/opt/local/var/run/mysql57 に mysql.sock を touch し chown _mysql しておく。

それでもまだ同じのが出るので、ググったら、

`secure-file-priv` とやらを設定する必要がありそうだ。

+secure-file-priv=/opt/local/var/db/mysql57/files

/opt/local/var/db/mysql57/files を 770 に設定。


再度実行。

2017-07-17T04:45:09.183964Z 0 [Warning] Setting lower_case_table_names=2 because file system for /opt/local/var/db/mysql57/ is case insensitive
2017-07-17T04:45:09.184760Z 0 [Warning] You need to use --log-bin to make --binlog-format work.
 100 200 300 400 500
 100 200 300 400 500
2017-07-17T04:45:11.707991Z 0 [Warning] InnoDB: New log files created, LSN=45790
2017-07-17T04:45:11.755651Z 0 [Warning] InnoDB: Creating foreign key constraint system tables.
2017-07-17T04:45:11.815967Z 0 [Warning] No existing UUID has been found, so we assume that this is the first time that this server has been started. Generating a new UUID: b7c42696-6aaa-11e7-ab9a-2c830cb15594.
2017-07-17T04:45:11.836664Z 0 [Warning] Gtid table is not ready to be used. Table 'mysql.gtid_executed' cannot be opened.
2017-07-17T04:45:12.110979Z 0 [Warning] CA certificate ca.pem is self signed.
2017-07-17T04:45:12.140515Z 1 [Note] A temporary password is generated for root@localhost: yMPR*aN3jPs.
2017-07-17T04:45:50.765696Z 0 [Warning] Insecure configuration for --secure-file-priv: Location is accessible to all OS users. Consider choosing a different directory.
2017-07-17T04:45:50.766060Z 0 [Note] /opt/local/lib/mysql57/bin/mysqld (mysqld 5.7.17-log) starting as process 10452 ...
2017-07-17T04:45:50.768018Z 0 [Warning] Setting lower_case_table_names=2 because file system for /opt/local/var/db/mysql57/ is case insensitive
2017-07-17T04:45:50.768420Z 0 [Warning] You need to use --log-bin to make --binlog-format work.
2017-07-17T04:45:50.769777Z 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2017-07-17T04:45:50.769812Z 0 [Note] InnoDB: Uses event mutexes
2017-07-17T04:45:50.769827Z 0 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier
2017-07-17T04:45:50.769898Z 0 [Note] InnoDB: Compressed tables use zlib 1.2.11
2017-07-17T04:45:50.770818Z 0 [Note] InnoDB: Number of pools: 1
2017-07-17T04:45:50.770961Z 0 [Note] InnoDB: Using CPU crc32 instructions
2017-07-17T04:45:50.773358Z 0 [Note] InnoDB: Initializing buffer pool, total size = 512M, instances = 1, chunk size = 128M
2017-07-17T04:45:50.821204Z 0 [Note] InnoDB: Completed initialization of buffer pool
2017-07-17T04:45:50.880435Z 0 [Note] InnoDB: Highest supported file format is Barracuda.
2017-07-17T04:45:50.897177Z 0 [Note] InnoDB: Creating shared tablespace for temporary tables
2017-07-17T04:45:50.897331Z 0 [Note] InnoDB: Setting file './ibtmp1' size to 12 MB. Physically writing the file full; Please wait ...
2017-07-17T04:45:50.933367Z 0 [Note] InnoDB: File './ibtmp1' size is now 12 MB.
2017-07-17T04:45:50.934408Z 0 [Note] InnoDB: 96 redo rollback segment(s) found. 96 redo rollback segment(s) are active.
2017-07-17T04:45:50.934428Z 0 [Note] InnoDB: 32 non-redo rollback segment(s) are active.
2017-07-17T04:45:50.934644Z 0 [Note] InnoDB: Waiting for purge to start
2017-07-17T04:45:51.059136Z 0 [Note] InnoDB: 5.7.17 started; log sequence number 2534561
2017-07-17T04:45:51.059358Z 0 [Note] InnoDB: Loading buffer pool(s) from /opt/local/var/db/mysql57/ib_buffer_pool
2017-07-17T04:45:51.059568Z 0 [Note] Plugin 'FEDERATED' is disabled.
2017-07-17T04:45:51.061839Z 0 [Note] InnoDB: Buffer pool(s) load completed at 170717 13:45:51
2017-07-17T04:45:51.069359Z 0 [Note] Found ca.pem, server-cert.pem and server-key.pem in data directory. Trying to enable SSL support using them.
2017-07-17T04:45:51.069387Z 0 [Note] Skipping generation of SSL certificates as certificate files are present in data directory.
2017-07-17T04:45:51.070018Z 0 [Warning] CA certificate ca.pem is self signed.
2017-07-17T04:45:51.070075Z 0 [Note] Skipping generation of RSA key pair as key files are present in data directory.
2017-07-17T04:45:51.077066Z 0 [Note] Server hostname (bind-address): '127.0.0.1'; port: 3306
2017-07-17T04:45:51.077602Z 0 [Note]   - '127.0.0.1' resolves to '127.0.0.1';
2017-07-17T04:45:51.077688Z 0 [Note] Server socket created on IP: '127.0.0.1'.
2017-07-17T04:45:51.093262Z 0 [Note] Event Scheduler: Loaded 0 events
2017-07-17T04:45:51.093377Z 0 [Note] Executing 'SELECT * FROM INFORMATION_SCHEMA.TABLES;' to get a list of tables using the deprecated partition engine. You may use the startup option '--disable-partition-engine-check' to skip this check. 
2017-07-17T04:45:51.093393Z 0 [Note] Beginning of list of non-natively partitioned tables
2017-07-17T04:45:51.106188Z 0 [Note] End of list of non-natively partitioned tables
2017-07-17T04:45:51.106279Z 0 [Note] /opt/local/lib/mysql57/bin/mysqld: ready for connections.
Version: '5.7.17-log'  socket: '/opt/local/var/run/mysql57/mysql.sock'  port: 3306  Source distribution
 $ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.17-log

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

動いた。大変だった。

*1:古いバージョンとか簡単に入って便利

2017-06-18

[][] vim-noqalign を作った

同僚氏が noqalign と言うツールを作った。


今のお仕事では Python の `__init__.py` にモジュールを import する。

import だけしてるので flake8 対策として `# noqa: F401` をつけるが、これを面倒と感じた同僚氏が自動で入れるようなツールを作った。


お仕事が佳境な状況だったけど、息抜きに Vim plugin から使えるようにした。

GitHub - heavenshell/vim-noqalign: noqalign wrapper for Vim

超便利!

2017-05-05

[] vim-prettier を作った

tl;dr

GitHub - heavenshell/vim-prettier: Prettier-Eslint-Cli for Vim


Prettier と言う JavaScript のフォーマッターがある。

こいつ単体ではお仕事で設定している ESlint の設定とは関係なくフォーマットするが、prettier-eslint と言うのがあり、これを使えば ESLint の設定通りにフォーマットしてくれる。


で、フォーマッターはエディタから設定したくなる。

オフィシャルのドキュメントには以下のように使えばいいよとある。

autocmd FileType javascript set formatprg=prettier\ --stdin
autocmd BufWritePre *.js :normal gggqG

お手軽だけど、同期的に動く。

そして Prettier は速くはない。

当然 Vim から使えば、その間ブロックされる。

おそらく neoformat とやらも同期で動く。

https://github.com/sbdchd/neoformat/blob/master/autoload/neoformat.vim#L82

Golang の Fmt なんかは速いので同期でも問題ないが、 Prettier は同期では辛い。


というわけでいつも通り Vim の job を使って作った。

ただ、いくつか罠があった。


出力先がバッファ

job は出力先にバッファなファイルを選択できる。

この場合はフォーマットした結果をバッファにて書き換えたいから、バッファを選択してみた。

let s:job = job_start(cmd, {
        \ 'callback': {c, m -> s:callback(c, m)},
        \ 'err_cb': {c, m -> s:error_callback(c, m)},
        \ 'in_io': 'buffer',
        \ 'in_name': file
\ })

こうすると、現在開いているバッファ(file で指定しているバッファ)に追記される。

追記じゃなくて、書き換えてほしい。


出力先がバッファ
let s:job = job_start(cmd, {
        \ 'callback': {c, m -> s:callback(c, m)},
        \ 'err_cb': {c, m -> s:error_callback(c, m)},
        \ 'in_io': 'file',
        \ 'in_name': file
\ })

こうすると、現在開いているファイルをバックグラウンドでフォーマットした結果に書き換える。

現在開いているバッファは書き換わる前の状態。

この状態で :e! でファイルを開き直したら、書き換わった状態に変わる。

当然ファイルが書き換わってるので、フォーマットを実行したらファイルが保存される。


前に作った ESLint の --fix モードもこの方式で実装した*1

vim-frontier/eslint.vim at master ? heavenshell/vim-frontier ? GitHub


Vim-Swift などもこんな感じで行っている。

vim-swift/fmt.vim at master ? dictav/vim-swift ? GitHub


保存と同時に実行するという制約をつければいいが、なんか釈然としない。

せっかく stdin で受け取れるので、フォーマットを実行したら、保存されずにそのままにしてほしい。

他にないかなと探したら、 id:haya14busa さんの GitHub - haya14busa/vim-gofmt: Formats Go source code asynchronously with multiple Go formatters. でおおっと思った実装がされていた。

Add silent for avoid message by heavenshell ? Pull Request #1 ? haya14busa/vim-gofmt ? GitHub *2

tmp なファイルにフォーマット後のコードを吐き出して、ファイルを読み込んで現在のバッファを全て消してバッファを書き換えていた。


prettier は stdout でフォーマット結果を返してくれるので、job の callback で受け取ってリストに追加、 exit_cb で追加されたリストの値で復元とすれば行けた。

*1:あとで書き換えるかも

*2:最新のコミットで改善されてたようだけど

2017-03-31

[][] Vim を mocha から実行する際の注意

QuickRun を利用して、 Vim から各種テストフレームワークを実行してる。

GitHub - heavenshell/vim-quickrun-hook-unittest: Quickrun hook for enable to unittest by selecting method.

mocha も実行できるようにしたけど、mocha.opts に自分のプロジェクトのヘルパーファイル名 js ファイルを読み込んでるとエラーになるのがわかった。

--require test/helper.js
--require babel-register
--recursive

こんなのがあると、テストの実行が、package.json とかがあるディレクトリから実行すると問題ないが、テストファイルのあるディレクトリから実行すると、 `test/helper.js` が実行ファイルからの相対パスとなって、そんなファイルないと怒られる。


結局アドホックに mocha.opts の内容を QuickRun 実行時に読み込んで `--require` がある行を mocha の cli オプションに渡すことにした。

そしてファイルが `.js` で終わるファイルは多分ローカルプロジェクトのものだろうという雑な感じで、フルパスに変換してやる。


ということで、テストファイルの特定のテストを実行できるようになった。

2017-02-12

[] SQLALchemy seed を作った

お仕事で Django を使ってて便利だなーと思ったのが fixtures。

初期データを突っ込むのに便利。

Providing initial data for models | Django documentation | Django


yaml で初期データの定義を書いて、コマンド経由で DB にデータを入れられる。

Flask で似たようなことできないだろうかと探したら、Flask-Fixtures があったが、これはテストデータを入れるのを想定している模様。

Add load_fixtures_from_file by RoPP ? Pull Request #21 ? croach/Flask-Fixtures ? GitHub という PR もあるが放置されている。


また Flask-SQLAlchemy に依存しているので、素の SQLALchemy を使ってる場合は使えない。

SQLAlchemy-Fixtures というものがあるが、Factory_boy を使えとある。


もう一つの方法としてはマイグレーションツールの Alembic の中で bulk_insert を使って初期データを入れる。

が、autogenreate を使う場合を想定すると、マイグレーションファイルとは別にしたい。


というわけで SQLAlchemy と PyYaml*1だけに依存しているものを作った。

GitHub - heavenshell/py-sqlalchemy_seed: Simple data seeder using SQLAlchemy.

こんな感じのディレクトリ構成。

.app
  /fixtures/pictures.yaml
  models
  /models.py
  /db.py
  views.py

Seed ファイルを書く。

書き方は Django と同じ(多分)。

- model: app.models.picture.Picture
  id: 1
  fields:
    name: spam.jpg
    title: Spam
    description: spam description
- model: app.models.picture.Picture
  id: 2
  fields:
    name: ham.jpg
    title: Ham
    description: ham description
- model: app.models.picture.Picture
  id: 3
  fields:
    name: beacon.jpg
    title: Beacon
    description: beacon description

こんな感じで、 yaml ファイルを書く。

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker

engine = create_engine('sqlite:///./db.sqlite', convert_unicode=True)
Base = declarative_base()
Base.metadata.bind = engine
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
session = scoped_session(Session)

モデルはこんな感じ。

from sqlalchemy import Column, Integer, String
from .db import Base


class Picture(Base):
    __tablename__ = 'pictures'
    id = Column(Integer, primary_key=True)
    name = Column(String(254), nullable=False)
    title = Column(String(254), nullable=False)
    description = Column(String(254), nullable=False)

    def __init__(self, name, title, description):
        self.name = name
        self.title = title
        self.description = description

呼び出し側はこんな感じ。Flask-Script を使ってみる。

import os
from flask_script import Manager, prompt_bool
from sqlalchemy_seed import load_fixtures, load_fixture_files
from app import create_app
from app.models.db import session


@manager.command
def loaddata(filename=''):
    """Load seed data."""
    if filename == '':
        return

    from app.models.db import session
    path = os.path.join(app.root_path, 'fixtures')

    fixtures = load_fixture_files(path, [filename])
    load_fixtures(session, fixtures)

動かしてみる。

$ python manage.py loaddata -f pictures.yaml
$ sqlite3 db.sqlite
SQLite version 3.16.2 2017-01-06 16:32:41
Enter ".help" for usage hints.
sqlite> select * from pictures;
1|spam.jpg|Spam|spam description
2|ham.jpg|Ham|ham description
3|beacon.jpg|Beacon|beacon description
sqlite>

便利になった。

*1:多分JSON ファイルも行けると思うし、機能は入れてるが未確認

2017-01-02

[][] Python の Flake8 と Mypy のプラグイン作った

tl;dr

Flake8 と Mypy のチェックが動く Vim プラグイン作った。


Flake8 のプラグインは何個あるんだって感じだが、pyflakes-vim はとっくに deprecated だし、Python3 の type-hint な文法は解釈できないし、既存の Flake8 なプラグイン(Syntactic, neomake) は保存をしないとチェックが走らないし、Syntactic に至っては遅いし、khuno.vim は保存しなくてもリアルタイムにチェックしてくれるが、QuickFix 使わず独自のバッファでエラーリスト管理してるし、どれも気に入らないから結局自分で作った。

GitHub - heavenshell/vim-snowflake: An asynchronous Python source code checker for Vim.


特徴

例によって job と channel で動作している。

デフォルトは Flake8 だが、frostedpylama もパーサー書けば動く。

Flake8 は stdin から受け取るオプションがあるので、バッファをそのまま流し込んでるだけ。

そのため保存しなくてもチェックが走る。

InsertLeave や TextChange あたりに autocmd を設定すれば良い。

Python3 な venv で Flake8 を入れたら当然 type-hint なものもエラーではなくて、解釈してくれる。

MyPy

せっかく type-hint が正しく通るようになったから、型チェックもやらしたい。

現状 Mypy で Vim と言ったら @_achiku さんの以下のエントリにあるよう、Syntactic を使うしか無い模様。

syntasticとmypyでvimからPython型アノテーション確認


Mypy にはオプションに stdin はなく、Guido 御大も stdin オプションをつけたく無いと言われてるので、入ることはなさそう。

Feature request: checking files on stdin ? Issue #2119 ? python/mypy ? GitHub

その代わりに `--shadow-file` というドキュメントに書いてない隠しオプションあるから、そっち使ってねとある。

Feature request: checking files on stdin ? Issue #2119 ? python/mypy ? GitHub


`-shadow-file` の説明ソースコード内に書いてある。

mypy/main.py at master ? python/mypy ? GitHub

cp app.py tmp.py
mypy --incremental --silent-imports --show-column-numbers --shadow-file app.py tmp.py 

と動かすと、チェックされない。

mypy: error: Missing target module, package, files, or command.

正しく動かすには以下のようにする。

mypy --incremental --silent-imports --show-column-numbers --shadow-file app.py tmp.py app.py

チェックする対象のファイルが必要な模様。

app.py を指定してるんだからそれで良さそうなのに、コード読むと必要みたい。


tmp.py には Vimバッファの内容を読み取って、 `tempname()` でテンポラリにファイルを作成したやつを指定する。


これで type-hint な Python コードも静的チェックができるようになった。

アンドキュメントなやつだし、使えなくなる可能性高いけど便利。


実際に Flake8 と Mypy を同時に job を使いつつ TextChange イベントとかでフックすると重そうだけど、実際のプロダクト書くときに使ってみてドッグフーディングしてみる。


こういうツールを作ってる人にはエディタからの連携が楽になるので是非 stdin をサポートしてほしい。