imadedede のブログ

今出川潤の出張所。

VS Code の Pylint で venv 仮想環境に追加したモジュールを読み込む

先日の記事の続き。

imadedede.hatenablog.com

使用環境は LinuxMint 19.1 tessa で apt インストールの Python3.6.8 + Pylint3 1.8.3 + astroid 1.6.0。Microsoft Visual Studio Code 1.36.0 には Python 拡張機能 2019.6.22090 を追加している。

問題

今作ってる Python プロジェクトでは venv で仮想環境を作って pip3 でパッケージを追加している。しかし、先日の方法でシステムの Pylint を VS code に設定すると、仮想環境にインストールした PyPI パッケージを読み込んでくれず、 VS Code 上で常に赤線が引かれてしまう。非常に鬱陶しい。ではどうするか。

解決策

Pylint が仮想環境のパッケージも読み込むように設定する。

1. コマンドライン上で仮想環境をアクティブにする

標準ライブラリの venv を使って仮想環境を作りアクティブ化する。例では仮想環境のフォルダを project としている。

$ python3 -m venv project
$ . project/bin/activate

2. プロジェクトのルートに .pylintrc を作る

プロジェクトに pylint の設定ファイルを追加する。

(project) $ pylint --generate-rcfile > .pylintrc

3. init-hook を編集

Pylint 設定を編集する。

# init-hook のコメントアウトを外してライブラリを読み込むよう設定
init-hook="import sys; sys.path.append('./project/lib/python3.6/site-packages')"

Pylint の起動前動作に仮想環境のパッケージのインストール先ディレクトリを追加する。今回の環境では ./project/lib/python3.6/site-packages になったが、各自の環境で読み替えてもらえれば。

これで、仮想環境のパッケージを読み込んだ状態で VS Code を起動できる。余計な赤線も入らない。

余談

Pylint のデフォルト設定を追加すると、今度はちょっとした記述に緑線が引かれるようになり、これはこれで少しうるさく感じてしまう。しかし、まあ、お行儀良く書くのが Python の流儀なので、普段からこのうるささを黙らせ続けるくらいの方が丁度いいのかもしれない。

参考

qiita.com

Python3 の csv.writer で改行コードを LF にしたいときは lineterminator を変更する

ここ読んで見つけた。

stackoverflow.com

使用環境は LinuxMint 19.1 tessa で Python3.6.8。

デフォルトの csv.writer の改行コードは CR/LF で出力される

例えば以下のようなコードがある。

import csv

with open('hoge.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['one', 'two', 'three'])

Python3 の標準ライブラリを使った簡単な CSV 書き出しコードだが、これでそのまま書き出すと改行コードは CR/LF になる(Excel 互換のため?)。これを LF にしたい。しかし公式ドキュメントでは newline='' を使えと書いてある。ではどうするか。

lineterminator='\n' を指定して LF に変更する

以下のように lineterminator という終端処理パラメータを変更する(ドキュメントはこちら)。

import csv

with open('hoge.csv', 'w', newline='') as f:
    writer = csv.writer(f, lineterminator='\n') # LF にしたいので '\n' を指定する。
    writer.writerow(['one', 'two', 'three'])

これで改行コードが LF の CSV ファイルが出力される。

VS Code で Python 拡張機能を使うときの Pylint 設定

ここを読めばわかる。 stackoverflow.com

今回はここの解決策から引用している。

使用環境は LinuxMint 19.1 tessa で Python, Python3 共に apt から最新版をインストール済み。Microsoft Visual Studio Code 1.36.0 には Python 拡張機能 2019.6.22090 を追加している。

VS Code で Pylint の設定をしていく

VS CodePython 拡張機能を入れて Python のソースファイルを開くと Pylint の設定を求められる。無いなら入れろ、と言われる。しかし LinuxMint 19.1 では Pylint は apt にある。できればこれを使いたい。その設定を VS Code にしていく。

1. Pylint の場所を探す

which pylint する。例えばそれが /usr/bin/pylint だったとする。

2. VS Code の設定を変更する

VS Code で設定を開き、検索窓に python.linting.pylintpath と入れる。

そしてその項目に、さっき which pylint した場所(今回は /usr/bin/pylint )を入れる。

これで OK 。pip install pylint を毎回しなくてもいい。

自分は Python3 をメインに使っているので Pylint3 を設定してます。

7/12 追記

venv で仮想環境を作ったとき Pylint が追加パッケージの利用をいちいち赤線で注意してくる問題に対応する方法。

imadedede.hatenablog.com

Apache + pukiwiki と Tomcat + GitBucket をポート80番で共存させる

Linuxデスクトップ環境で、ゆるく wiki と git を運用したい。というわけで、 localhostpukiwiki と GitBucket をインストールして起動してみる。

方針として、ゆるく運用&構築したいので、なるべく手軽にインストールして、デフォルト設定に近い状態で使う。

環境は Linux Mint 19 Tera の Mate で。それでは行ってみよう。

Apache + pukiwiki

まず Apache による wiki から設定していく。

インストールするパッケージ

必要なパッケージのインストールは Synaptic で行う。 GUI で楽したい。下記のパッケージをインストール指定して適用する。

インストールが終わったら http://localhost がすでに動いている。アクセスして動作を確認。

設定ファイル確認

主な設定があるファイルは /etc/apache2/apache2.conf にある。今回はこれは特にいじらないが、ざっくり見ておく。

また /etc/apache2/envvars を見て APACHE_RUN_USERAPACHE_RUN_GROUP が何になっているかを確認しておく。Linux Mint は元々は UbuntuDebian 系なので、これは www-data になっている。

念の為、

cat /etc/group

しておいて、 www-data ができていることを確認しておく。

pukiwiki のダウンロードと所有権の変更

pukiwiki をここからダウンロード。現在の最新版は1.5.1。 pukiwiki.php.ini の変更などインストールに関することは該当ページを参照。動作確認するだけならいじる必要はない。

解凍後にできたフォルダの名前を pukiwiki に変更。そして pukiwiki フォルダを Apache のデータ用フォルダにコピーする

sudo mv pukiwiki /var/www/html/

そして /var/www 以下の所有権の変更。

sudo chown -R www-data.www-data /var/www

動作確認

sudo systemctl restart apache2

apache を再起動。 http://localhost/pukiwiki/ にアクセスして、 pukiwiki が動いていることを確認する。

Tomcat + GitBucket

Apache の次は、 Tomcat で Git サーバを構築する。

インストールするパッケージ

Synaptic で以下のパッケージをインストール指定して適用。

  • default-jre
  • Tomcat8
  • Tomcat8-admin

Tomcat Web アプリケーションマネージャも一緒に入れる。なくてもいいけど、ゆるく運用するにはこういうの欲しい。

設定ファイル変更

/etc/tomcat8/tomcat.users.xml を編集する。コメントアウトを有効化して、管理人ロールとアカウントを追加。

 <role rolename="admin-gui"/>
 <user username="tomcat" password="password" roles="tomcat,admin-gui"/>

ポート設定を変更。 /etc/tomcat8/server.xml を編集して、デフォルトのポートを 8080 から 8081 に変更。

    <Connector port="8081" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

これは必要ないけど、8080のままだと他のアプリのデフォルト設定とこの先ガンガンぶつかっていってしまうため。

GitBucket のダウンロードと所有権の変更

GitBucket の Release から gitgucket.war のダウンロード。http://localhost:8081/manager/ にアクセスしてWebアプリケーションマネージャから gitgucket.war ファイルをアップロード。そして現段階では起動しないことを確認しておく。

念の為、ウェブAPPマネージャからアップロードされた先のフォルダを見ておく。

ls -al /var/lib/tomcat8/webapps

gitbucket.war がちゃんとコピーできていて、かつ所有者が tomcat8 になっていることを確認する。

次に GitBucket のデータ用フォルダを作って、所有権をtomcat8に明け渡す。

sudo mkdir /var/lib/tomcat8/.gitbucket
sudo chown tomcat8:tomcat8 /var/lib/tomcat8/.gitbucket 

こうしてからだとWebアプリケーションマネージャでGitBucketを起動できる。

GitBucketがどこをデータ用フォルダとするかは環境によって異なるらしい。Ubuntu 16.04なら /usr/share/tomcat8/.gitbucketからしい(こちらのブログを参照)。 Webアプリケーションマネージャから一度起動に失敗しておくと、 /var/log/tomcat8/localhost.*.log にエラーログが残る。そのエラーログを読めば、どこにフォルダを作ろうとして失敗したのかがわかる。わかったら、あとはそこに作って所有権を渡せばいい。

まあ普通はいちいち失敗させて確認するより、予め環境変数によってフォルダを設定すると思う(こちらのブログ参照)。今回はゆるく運用ということでこのやり方はしないけど、ちゃんと運用するならちゃんとやろう。

動作確認

sudo systemctl restart tomcat8

tomcat を再起動。 http://localhost:8081/gitbucket/ にアクセスしてみる。動作していたらOK。

ポート80番で ApacheTomcat を共存させる

さて、ここからが本番。Tomcat から GitBucket を起動したものの、このままだといちいちURL にポート番号をいれる必要がある。これは面倒。なので、http のデフォルトポート80番からアクセスできるようにする。

方針としては、特定の URL にアクセスしたときのみ、Apache から Tomcat に切り替えてアクセスする。

Tomcat 側の設定

/etc/tomcat8/server.xml を編集する。AJP に関する部分がコメントアウトされているので、これを外して有効化する。

    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Apache 側の設定

いくつかのモジュールを有効化する。

sudo a2enmod proxy proxy_http proxy_ajp

有効化したら、 /etc/apache2/mods-enabled/proxy.conf にファイルができているので、アクセスして編集する。 <IfModule> タグの内側に以下のように記述。

ProxyPass /gitbucket/ ajp://localhost:8009/gitbucket/
ProxyPassReverse /gitbucket/ ajp://localhost:8009/gitbucket/

動作確認

apachetomcat の再起動を行う。

sudo systemctl restart apache2 tomcat8

http://localhost/gitbucket/ にアクセスしてみる。アクセスできるならOK。

もちろん、既存の http://localhost/pukiwiki/ の方へも問題なくアクセスできることも確認しておく。

感想

apachetomcat もちゃんと使うのは初めてだったため、とても大変だった。 所有権に関するトラブルでだいぶ悩んだんだけど、検索する限りよくあるトラブルらしいので、今後とも気をつけていきたい。

それはそれとして、 Linux MintLinux デスクトップ環境としてかなり使いやすく感じた。元が Ubuntu でさらにその元は Debian なので、情報が検索してヒットしやすいし、様々なツールが既に用意されている。強力なバックアップツールまでついているので、ゆるく運用するにはもってこい。

というわけで、しばらくは Linux Mint を使ってみようと思った。

続・C言語で文字列を逆にする

昨日の記事の続き。
寝る前にふと、文字列長を取得する部分を改善できるのではないか、と思いついた。
しかしそれなら長さ0の文字列にどう対応するべきか。
しかたないので関数にして括り出して対応することにした。
以下、改善版のコード。

// 文字列を逆にする
// どうせなら string.h なしで
#include <stdio.h>

// 文字列のポインタを受け取って、その文字列の並びを逆にする
void strrev(char s[]){
	char temp = '\0';
	char *head = s;
	char *tail = s;
	
	// 長さ0の文字列なら操作せず返す
	if(*head == '\0'){ return; }

	// 文字列の最期の文字のポインタを取得する
	while( *(tail+1) != '\0'){ tail++; }

	// 逆にする
	while(head != tail){
		temp = *head;
		*head = *tail;
		*tail = temp;
		
		head++;
		if(head == tail){ break; }
		tail--;
	}
	return;
}

// a.out の後に続く文字列を逆順にする
int main(int argc, char* argv[]){
	char *s = argv[1];
	// 引数が足りないなら比較しない
	if(argc <= 1){ return 0; }

	// 操作前
	printf("Input: %s\n", s);
	// 文字列を逆にする
	strrev(s);
	// 操作後
	printf("Output: %s\n", s);
	return 0;
}

主な変更点は、文字列の長さを測らなくなったこと。
これによって int 型1つ分のメモリ消費がなくなった(その分文字列ポインタが2つ増えた)のと、入れ替えられる文字列の長さが int 型最大値に依存しなくなった。後者は明確な利点ではなかろうか。
計算量は特に変わらず O(n) のオーダのまま。
実行結果はこちら。

imadedede$ ./a.out a
Input: a
Output: a
imadedede$ ./a.out ab
Input: ab
Output: ba
imadedede$ ./a.out abc
Input: abc
Output: cba
imadedede$ ./a.out abcd
Input: abcd
Output: dcba

問題ない。これでよし。

追記:最新版はこちら

C言語で文字列を逆にする

最近 Joel on Software を紙で読んでいる。
その中に気になる一節があった。20章「採用面接ゲリラガイド」の中のこんな文章だ。
(※書籍版での更新日時は2004年6月4日となっており少し新しい。さらに更新された web 版の日記はこちら

4.プログラミングの質問
面接のこの部分には一番時間をかけるべきだ。私は候補者にC言語で(あるいは彼らがなじんでいる言語なら何でもいい)小さな関数を書くように求める。
以下に、私がよく出す問題の例を挙げる。

  1. 文字列をその場で逆にする
  2. 連結リストを逆にする
  3. バイトデータの中で経っているビットの数を数える
  4. 二文探索
  5. 文字列の中で同じ文字が一番長く続くところを見つける
  6. atoi
  7. itoa(スタックやstrrevを使う必要があるため、良い問題だ)

「文字列を逆にするのか・・・strlen() で計って、 malloc() をして・・・」
と考えながら読み進めると、まさにそれを先読みしたかのようなこんな記述が。

(中略)
1.の「文字列をその場で逆順にする」については、私がこれまで面接した候補者はみんな1度目には間違ったやり方をした。例外なく、彼らはもう1つのバッファを用意して、そこに逆順の文字列を作ろうとする。問題は、そのバッファのメモリ領域を誰が割り当てるのか? 誰がそのバッファを開放するのか?

間違ったやり方らしい。
そしてこう続いた。

この質問を何ダースもの候補者に出して気づいた興味深い事実がある。C言語を知っていると思っている人たちの多くが、メモリやポインタを本当には理解していない。彼らは単にそれが分からないのだ。そういう人たちがプログラマとして働いているというのも驚きだが、実際そうなのだ。この質問で候補者を判定する方法はいくつかある。

・・・と、いうことは、ポインタを使えばもっと早くメモリ消費も短くなるのかな?
そんなわけで、書いてみた。

// 文字列を逆にする
// どうせなら string.h なしで
#include <stdio.h>

// a.out の後に続く文字列を逆順にする
int main(int argc, char* argv[]){
	int len = 0;
	char temp = '\0';
	char *s = argv[1];
	char *head = NULL;
	char *tail = NULL;

	// 引数が足りないなら比較しない
	if(argc <= 1){ return 0; }

	// 操作前
	printf("Input: %s\n", s);
	
	// 文字列の長さを取得する
	len = 0;
	head = s;
	while(*head != '\0'){ head++; len++; }

	// 逆にする
	head = s;
	tail = &s[len-1];
	while(head != tail){
		temp = *head;
		*head = *tail;
		*tail = temp;
		
		head++;
		if(head == tail){ break; }
		tail--;
	}
	
	// 操作後
	printf("Output: %s\n", s);
	return 0;
}

実行結果はこんな感じ。

imadedede$ ./a.out a
Input: a
Output: a
imadedede$ ./a.out ab
Input: ab
Output: ba
imadedede$ ./a.out abc
Input: abc
Output: cba
imadedede$ ./a.out abcd
Input: abcd
Output: dcba

これだと、文字数が1文字でも奇数でも偶数でも入れ替えられる。
元の文字列が破壊されてしまうけど、特に制限はなかったからこれでも間違いではないはず。
・・・実のところ、書き始めでは文字列の長さをとった後で head ポインタの初期化を忘れており、そのデバッグに30分ほどかかってしまった。
まだまだ精進が足りませんなあ。
まあでも、たまにはこういうことを考えてみるのも一興ということで。

追記:最新版はこちら

NMeCab を .NET アプリに組み込んで形態素解析

MeCab というものがある。
誤解される恐れがあるのを承知で、かなりざっくりといいかげんに説明すると、これを使うと日本語の漢字の読みとかを文脈に沿って解析してくれる。

MacOS X には(10.5以降から?)標準で入っていて Spotlight 検索などに使われているし、ゆっくり MovieMaker はこれを使っていることが売りの一つになっている。

NMeCab とは、その MeCabC# で書き直されたもの。「ゆっくいち!」は C# 2.0 で開発しているので、試しに NMeCab を使ってみた。

準備

ダウンロードしてくる

NMeCab 配布ページにて最新版をダウンロード。

解凍して、フォルダ内からいくつかテストに必要なモノをもってくる。

現時点での最新版は 0.06.3 。解凍すると NMeCab0.06.3 というフォルダができる。

NMeCab.dll をとってくる

NMeCab0.06.3/trunk/bin にある。 NMeCab の核。

MMF 版もあるけど、これを使うなら C# 4.0 でないといけない。今回は C# 2.0 環境でやりたいのでとりあえずここにあるものを使う。

元々の MeCab に対してバインディングしているわけではないので、この DLL で機能が完結している。

WindowsFormsSample.exe.config をとってくる

NMeCab0.06.3/trunk/bin にある。要は DLL と同じ場所の XML 。辞書のパス指定に使う。

XML 内のシステム辞書を指定するパスの ..\dic\ipadic を、今回は .\dic\ipadic に変更。そしてファイル名を NMeCabTest.exe.config に変更する。

ダウンロードしたものには IPA 辞書が入っているが、自分で NAIST 辞書をダウンロードしてコンパイルしてこのファイルで指定すれば、 NMeCab で NAIST 辞書を使用することも可能。

NMeCab 自体には辞書のコンパイル機能はない。元々の MeCab を使わないといけない。

辞書をとってくる

NMeCab0.06.3/trunk/ にあるdicフォルダをフォルダごととってくる。
DLL の一つ上のフォルダにある。

とってきたものをあつめる

以上、用意したモノを一つのフォルダに集める。
最終的なフォルダ構成はこんな感じ。

temp
├─LibNMeCab.dll
├─NMeCabTest.cs
├─NMeCabTest.exe.config
└─dic
    └─ipadic
        ├─char.bin
        ├─dicrc
        ├─matrix.bin
        ├─sys.dic
        └─unk.dic

この LibNMeCab.dll と同じ場所に NMeCabTest.cs ファイルを作り、コーディングしていく。

テストコード

クラス名やコンストラクタが多少違うもの、基本的には他の MeCab バインディングと変わらない。

/*
 * NMeCab はクラス名が変更されている
 * MeCab::Tagger ⇒ NMeCab.MeCabTagger
 * MeCab::Node ⇒ NMeCab.MeCabNode
 * 
 * 詳しい実装やクラスメンバなどは NMeCab0.06.3/trunk/LibNMeCab/MeCabNode.cs など参照。
 * 
 * mono で使うなら、辞書ファイルパスのバックスラッシュをスラッシュに変更
 * コンパイルは
 * gmcs -r:module.dll use_module.cs
 * 
 */
using System;
using NMeCab;

public class NMeCabTest{
	static public void Main(){
		string sentence = "行く川のながれは絶えずして、しかも本の水にあらず。";
		
		// NMeCab はクラス名が変更されている
		MeCabTagger t = MeCabTagger.Create();
		
		// 形態素を一つずつたどっていく
		Console.WriteLine("形態素をひとつずつたどっていく");
		MeCabNode node = t.ParseToNode( sentence );
		while( node != null ){
			Console.WriteLine(node.Surface + "\t" + node.Feature);
			node = node.Next;
		}
		Console.WriteLine();
		
		// 一気に結果をもらう
		Console.WriteLine("一気に結果をもらう");
		string result = t.Parse( sentence );
		Console.WriteLine(result);
	}
 
}

コンパイル

DLL をライブラリとして指定するのを忘れずに。

csc \r:LibNMeCab.dll NMeCabTest.cs

あと実行前に EXE ファイル名と .exe.config のファイル名が対応していることを確認。

NMeCabTest.exe
NMeCabTest.exe.config

実行結果

コマンドを実行。

$ .¥NMeCabTest.exe
形態素をひとつずつたどっていく
行く川のながれは絶えずして、しかも本の水にあらず。	BOS/EOS,*,*,*,*,*,*,*,*
行く	動詞,自立,*,*,五段・カ行促音便,基本形,行く,イク,イク
川	名詞,一般,*,*,*,*,川,カワ,カワ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
ながれ	動詞,自立,*,*,一段,連用形,ながれる,ナガレ,ナガレ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
絶えず	副詞,一般,*,*,*,*,絶えず,タエズ,タエズ
し	動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
て	助詞,接続助詞,*,*,*,*,て,テ,テ
、	記号,読点,*,*,*,*,、,、,、
しかも	接続詞,*,*,*,*,*,しかも,シカモ,シカモ
本	名詞,一般,*,*,*,*,本,ホン,ホン
の	助詞,連体化,*,*,*,*,の,ノ,ノ
水	名詞,一般,*,*,*,*,水,ミズ,ミズ
に	助詞,格助詞,一般,*,*,*,に,ニ,ニ
あら	動詞,自立,*,*,五段・ラ行,未然形,ある,アラ,アラ
ず	助動詞,*,*,*,特殊・ヌ,連用ニ接続,ぬ,ズ,ズ
。	記号,句点,*,*,*,*,。,。,。
	BOS/EOS,*,*,*,*,*,*,*,*

一気に結果をもらう
行く	動詞,自立,*,*,五段・カ行促音便,基本形,行く,イク,イク
川	名詞,一般,*,*,*,*,川,カワ,カワ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
ながれ	動詞,自立,*,*,一段,連用形,ながれる,ナガレ,ナガレ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
絶えず	副詞,一般,*,*,*,*,絶えず,タエズ,タエズ
し	動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
て	助詞,接続助詞,*,*,*,*,て,テ,テ
、	記号,読点,*,*,*,*,、,、,、
しかも	接続詞,*,*,*,*,*,しかも,シカモ,シカモ
本	名詞,一般,*,*,*,*,本,ホン,ホン
の	助詞,連体化,*,*,*,*,の,ノ,ノ
水	名詞,一般,*,*,*,*,水,ミズ,ミズ
に	助詞,格助詞,一般,*,*,*,に,ニ,ニ
あら	動詞,自立,*,*,五段・ラ行,未然形,ある,アラ,アラ
ず	助動詞,*,*,*,特殊・ヌ,連用ニ接続,ぬ,ズ,ズ
。	記号,句点,*,*,*,*,。,。,。
EOS

解析できてる。

その他

ライセンスは GPL / LGPL のデュアルライセンス。アプリに組み込むのも悪くない。

そんな感じで、将来的には「ゆっくいち!」にも組み込みたい。