ラベル python の投稿を表示しています。 すべての投稿を表示
ラベル python の投稿を表示しています。 すべての投稿を表示

2022年9月7日水曜日

CEDEC でやってた補助動詞の漢字・ら抜き言葉の検出を実装してみた

CEDEC 2022 の「AIによる自然言語処理を活用したゲームシナリオの誤字検出への取り組み」で誤字検出方法が解説されてました。
セッションタイトルにある AI の部分は BERT 使った誤字検出の話で、サクッと実装は難しそうなので、比較的簡単そうな「補助動詞の漢字」「ら抜き言葉」の検出を実装しました。

実はこのセッションは昨年の CEDEC 2021 の続編で、昨年の内容も一部検証実装してました。
そのときの記事はこちらです。
CEDEC でやってた表記ゆれ検出をお試し実装してみた

今回の実装は昨年書いた tails-of-words に機能追加として実装してます。
なんですが、昨年のアップデートかつ実装難度としても高くないのであまりここに書くことがありません。。
(どういうロジックなのかは割とまんまな記事が出るのでそちらか、おそらく CEDiL に資料公開されると思うのでそちらを見て下さい。)

ら抜き言葉検出の改良

強いて書くとすると「ら抜き言葉」の検出がセッションのとは少し異なるくらいですね。
セッションでは「ら抜き言葉」の誤検出の例として以下のテキストがでてました。

  • 写真撮ってきたから見れ!
  • あれ?あれれれ?
  • 今こそ来たれ!
まず資料の通りに実装した場合の結果がこちら
なぜか1つ目と2つ目は「ら抜き言葉」とは判定されない解析結果でした(つまり誤検出しない)。
ツイートでは前処理の違いかもと言ってますが、 jumanpp に渡す前にテキストの正規化をしているのでもしかしてその違いがあるのかも?と思ったのですが、前処理で変化するような入力テキストではないので違いました。
使っている jumanpp or 辞書の違いでしょうか。(確認できないので、深追いはしない)

では、「今こそ来たれ!」ですが「れ」の直後の品詞が「特殊」となってます。
セッションで誤検出の例として上がったものすべて「特殊」品詞が直後にあるので、このパターンを「ら抜き言葉」から除外すれば良さそうです。

というわけで改良後はこうなります。

最後に
BERT とかそのへんもいつか触ってみたいです。
では。


2022年1月27日木曜日

wandbox-api version 0.9.38 をリリースしました

wandbox-api 0.9.38 をリリースしました。
0.9.37〜0.9.38 で新環境の Wandbox への対応と不具合修正をしています。
詳細はリリースノートを確認してください。
https://github.com/srz-zumix/wandbox-api/releases/tag/v0.9.38
https://github.com/srz-zumix/wandbox-api/releases/tag/v0.9.37

新しい Wandbox ではいくつかの言語が削除されました。(CoffeeScript,CMake,F#,Rill)
また、各言語のコンパイラーバージョンも大幅に削減されています。
これらに伴い、削除された言語、コンパイラーの alias コマンドを削除してます。

新しい環境になって、動作してない言語・バージョンもあるようなので拙作のステータスページなども確認してください。
https://srz-zumix.github.io/wandbox-status/

時間が取れたら、wandbox-builder に環境の修正 PR でもしていきたいです。
以上。

2021年10月1日金曜日

Tails of Words v0.3.0 リリース

シルバーウィークに久しぶりに連休を取ったので Tales of ARISE をやってました。まだクリアしてません。もうちょいでクリアだと思うのですが。なので最近はゲームばっかりしてます。

はい。では本題です。

tails-of-words 0.3.0 をリリースしました。
tails-of-words は「ブログズミ: CEDEC でやってた表記ゆれ検出をお試し実装してみた」で作成した表記ゆれ検出ツールです。
(なんだか某ゲームっぽいツール名ですね)

今回は前回ブログ時点からの差分を紹介したいと思います。

変更履歴
knp 対応

まず大きな変更点として knp(pyknp) に対応しました。
knp は juman の出力を入力に係り受けとかを解析してくれるものですが、
tails-of-words では knp の固有表現検出機能を使いました。

固有表現検出機能を使うと以下のように「奈良先端科学技術大学院大学」を1つの単語として検出できるようになります。


knp を有効にするには --knp オプションを付けます。実行環境に knp がインストールされている必要があるので、knp インストール済みの tails-of-words の docker container を使うのが楽だと思います。

コンフィグファイル対応

tails-of-words のオプション及び評価関数のパラメータをファイル指定できるようにしました。

log: DEBUG
html2test: True
exclude_ascii: True
exclude_alphabet: True
jaro_winkler: True
knp: False

# スコア計算の設定値
score_config:
  # 読みが同じ時にかける値
  same_yomi_scale: 1.2
  # 出現数の差の正規化数([0,1])にかける値
  occurrences_sacle: 1.0
  # 読みが同じかつ、長音が消えてる場合にかける値
  same_yomi_with_remove_long_vowel_scale: 0.8
  # 片方を内包し、末尾の長音の差分のみの場合にかける値
  diff_only_long_vowel_scale: 1.2
  # 片方を内包している場合にかける値
  inclue_other_one: 0.5
  
形態素解析のための正規化による表記ゆれ吸収問題を解決

pyknp で knp/jumanpp の出力する際に特定の文字列が出力フォーマットと衝突し、正しくパースできないので(たとえば半角スペースや @ などの記号)、解析前に全角文字に変換したりしているのですが、これにより当該文字の半角・全角の表記ゆれ検出ができないようになっていました。

これに対応するために、pyknp の結果の形態素の見出し(midasi)から正規化した文字をもとに戻す処理をいれました。
また固有表現検出に対応したことで「2021 年秋」と「2021 年秋」のようなスペースのゆれも検出できるようになりました。(スペース含んだ固有表現と認識されればの話)

jaro_winkler 編集距離に対応
レーベンシュタイン距離がデフォルトですが、ジャロ・ウィンクラー距離にも対応しました。
--jw,--jaro-winkler オプションを付けると切り替わります。
tails-of-words では編集距離を 0-1 の値に正規化して評価していますが、この値が変わるので表記ゆれの検出結果にも変化がでます。
0=不一致 1=一致


XML入力に対応
Blogger のバックアップ出力が xml だったので、このブログを解析するために対応しました。
xml ファイルの場合は text が解析対象になります。

標準入力に対応
パイプでつなげて処理したい。curl で html 取ってきて解析させたい。ということで対応。
以下のように --stdin-type で html を指定すれば html2text した文字列で解析できます。
curl -fsSL https://srz-zumix.blogspot.com/2021/09/cedec.html | tails-of-words --stdin-type html swing --exclude-alphabet --exclude-ascii -t 1 -
ASCII のみやアルファベットのみを除外するオプションを追加
記号のみやアルファベットのみの形態素は未定義語の hinsi_id になるようなので、
tails-of-words swing -i 6 hoge.txt のように -i オプションで解析対象の品詞を名詞(6)のみにすることで除外できます。(デフォルトは 名詞(6) と 未定義語(15))

GET と Get や get など大文字小文字の揺れを除外したい場合、これで実現可能だったのですが、未定義語にはカタカナ語なども入ることがあるため、このユースケースに対応するために --exclude-alphabet (--no-alpha) と --exclude-ascii (--no-ascii) オプションを追加しました。
最後に
とりあえず実装してみた前回バージョンからだいぶパワーアップしました。
実際に拙著やこのブログの表記ゆれを検出しようと使ってみたら、こうなりました。
編集距離にはまだ他の手法もあるようなので、そのうち対応してみたいです。

今回は以上。



2021年5月27日木曜日

[Debian] pyenv install で Missing the OpenSSL lib? で失敗する場合の対応と 3.0~3.10 全部入り Dockerfile 書いた

 「Missing the OpenSSL lib?」で検索するといろいろ対策方法が出てきますが、大体ここに書いてあるので自分の環境に合わせて方法を見れば良いと思います。
https://github.com/pyenv/pyenv/issues/950

本記事では「Debian」環境で対処した方法と、pyenv install で 3 系を全部入れてみた Dockerfile を紹介します。

対処方法

libssl-dev をアンインストールして libssl1.0-dev をインストールする。以上。
apt-get remove -y libssl-dev && apt-get install -y libssl1.0-dev

OpenSSL 1.1 に対応してないかららしいですね。
ちなみに 3.10 系は libssl1.0-dev だと逆にダメでした。

全部入り Dockerfile
# development env

ARG VERSION=3.7-stretch
FROM python:$VERSION
ARG VERSION

LABEL maintainer "srz_zumix <https://github.com/srz-zumix>"

SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN apt-get update -q -y && \
    apt-get install -y --no-install-recommends \
        make \
        build-essential \
        libssl-dev \
        zlib1g-dev \
        libbz2-dev \
        libreadline-dev \
        libsqlite3-dev \
        wget \
        curl \
        llvm \
        libncurses5-dev \
        xz-utils \
        tk-dev \
        libxml2-dev \
        libxmlsec1-dev \
        libffi-dev \
        liblzma-dev \
        && \
    apt-get remove -y libssl-dev && \
    apt-get update -q -y && \
    apt-get install -y --no-install-recommends libssl1.0-dev && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

ENV PATH="/root/.pyenv/bin:${PATH}"
RUN curl https://pyenv.run | bash && \
    echo "eval $(pyenv init -)" >> ~/.bashrc && \
    echo "eval $(pyenv virtualenv-init -)" >> ~/.bashrc
RUN	pyenv install -s "$(pyenv install -l | grep -e '\s3\.9[^0-9].*' | tail -1 | xargs echo -n)"
RUN	pyenv install -s "$(pyenv install -l | grep -e '\s3\.8[^0-9].*' | tail -1 | xargs echo -n)"
RUN pyenv install -s "$(pyenv install -l | grep -e '\s3\.7[^0-9].*' | tail -1 | xargs echo -n)"
RUN pyenv install -s "$(pyenv install -l | grep -e '\s3\.6[^0-9].*' | tail -1 | xargs echo -n)"
RUN pyenv install -s "$(pyenv install -l | grep -e '\s3\.5[^0-9].*' | tail -1 | xargs echo -n)"
RUN pyenv install -s "$(pyenv install -l | grep -e '\s3\.4[^0-9].*' | tail -1 | xargs echo -n)"
RUN pyenv install -s "$(pyenv install -l | grep -e '\s3\.3[^0-9].*' | tail -1 | xargs echo -n)"
RUN pyenv install -s "$(pyenv install -l | grep -e '\s3\.2[^0-9].*' | tail -1 | xargs echo -n)"
RUN pyenv install -s "$(pyenv install -l | grep -e '\s3\.1[^0-9].*' | tail -1 | xargs echo -n)"
RUN pyenv install -s "$(pyenv install -l | grep -e '\s3\.0[^0-9].*' | tail -1 | xargs echo -n)"

RUN apt-get remove -y libssl1.0-dev && \
    apt-get update -q -y && \
    apt-get install -y --no-install-recommends libssl-dev && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
RUN pyenv install -s "$(pyenv install -l | grep -v 'dev' | grep -e '\s3\.10[^0-9].*' | tail -1 | xargs echo -n)"  

以上。

2021年4月13日火曜日

Jinja2 cli な kamidana の additonals として slack 拡張を書いてみた

 Jinja2 は python のテンプレートエンジンです。
そして kamidana は Jinja2 の CLI です。
今回はふと思い立って Jinja2 で API 叩いてそのレスポンスを加工したいなーと思い、その練習の1つとして slack API を叩いてみました。

kamidana には --additionals オプションがあり、 .py ファイルを指定するとフィルターなどを拡張することができます。
最初は自分で Jinja2 組み込みで cli 書こうと思ってたんですが、↓を読んで additionals が便利そうだったので、この構成にしました。
悩んだ末に自分で自分の使うツールを作った話 - Qiitaできたもの

https://github.com/srz-zumix/yurumikuji

Slack = ゆとり・ゆるみ
Jinja2 = 神社
kamidana = 神棚
ということで、「ゆるみくじ」という名前にしました。
(ゆるみくじは干支などの動物の形したおみくじとか、ちょっとかわいいおみくじのことを指すみたいです。知らんかった)

実装したのはユーザー・ユーザーグループの情報取得と検索フィルターです。
使い方はリポジトリの README を読んでもらうとして・・・
例えば以下のテンプレートファイルを kamidana で処理すると

{{ ("srz_zumix" | slack_user_id | slack_user_info).profile.image_512 }}
{{ ("srz_zumix" | slack_user_id | slack_user_profile).image_512 }}
{{ "srz_zumix" | slack_user_id | slack_user_presence }}

このようになります。

https://secure.gravatar.com/avatar/a7614593f3f6f46b73da348c89beba81.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2Fdf10d%2Fimg%2Favatars%2Fava_0023-512.png
https://secure.gravatar.com/avatar/a7614593f3f6f46b73da348c89beba81.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2Fdf10d%2Fimg%2Favatars%2Fava_0023-512.png
active
書いてみた感想

使ってみると何に使えるかわかんないけど面白い。
レスポンスをそのまま返してるだけだけど、 Jinja2 の基本的な書き方で必要なものにアクセスできるので可能性を感じました。

とりあえず、しょっちゅう変わる所属を Slack のカスタムフィールドから取ってきて、メール署名のテンプレートでも作ってみようと思います。
あとは、本当にやりたかったことも簡単にできそうなことがわかったので kamidana additionals でやろうと思います。

では。

2020年10月5日月曜日

GitHub Actions のコンテナに setup-python したときの error while loading shared libraries 対応


python: error while loading shared libraries: libpython3.8.so.1.0: cannot open shared object file: No such file or directory

GitHub Actions で setup-python 使って python のテストをしていたのですが、とあるときから上記エラーが出るようになっていたので、以下の修正で対応しました。

修正前
 
zapcc-basic-test:
    runs-on: ubuntu-18.04
    container: docker://srzzumix/zapcc
    needs: prepare
    steps:
    - uses: actions/checkout@master
    - uses: actions/setup-python@v2
      with:
        python-version: 3.8
    - name: zapcc test
      run: |
        make -C test clean
        make -C test showcxxversion
        make -C test check_stdlib
        make -C test -j1 IUTEST_REQUIRE_PYTHON=1
        make -C test test
修正後
zapcc-basic-test:
    runs-on: ubuntu-18.04
    container: docker://srzzumix/zapcc
    needs: prepare
    steps:
    - uses: actions/checkout@master
    - uses: actions/setup-python@v2
      with:
        python-version: 3.8
   #### ここを追加 ####
    - name: Set Python environment variable
      run: echo "LD_LIBRARY_PATH=${{ env.pythonLocation }}/lib" >> $GITHUB_ENV
   ################
    - name: zapcc test
      run: |
        make -C test clean
        make -C test showcxxversion
        make -C test check_stdlib
        make -C test -j1 IUTEST_REQUIRE_PYTHON=1
        make -C test test

setup-python が pythonLocation 環境変数をセットしてくれるのでそれを使って LD_LIBRARY_PATH を設定します。

以上。

2020/10/6 追記

set-env コマンドは Deprecated だったので修正しました。
GitHub Actions: Deprecating set-env and add-path commands - GitHub Changelog

修正前: echo '::set-env name=LD_LIBRARY_PATH::${{ env.pythonLocation }}/lib'
修正後: echo "LD_LIBRARY_PATH=${{ env.pythonLocation }}/lib" >> $GITHUB_ENV

2020年8月31日月曜日

wandbox-api の CLI に C/CPP/Go/JavaScript/Nim/OpenSSL/Perl/Python/Ruby/TypeScript を追加しました

 表題のとおりですが以前公開した wandbox-api
C/CPP/Go/JavaScript/Nim/OpenSSL/Perl/Python/Ruby/TypeScript のラッパーコマンドを追加しました。(以前の記事はこちら「ブログズミ: Wandbox の CLI ツールを作ってみた」)
それぞれ言語ごと特有のオプションやコンパイラオプション・ランタイムオプションが使用可能かどうかに対応しています。

また、C/CPP/C++ であれば #include してるファイル(システムヘッダー除く)を自動的にファイルリストに追加する機能がありますが、Python の import や Ruby の require なども対応をしています。

OpenSLL は少し特殊でローカルで openssl を使うのと同じように使えるように工夫してます。
(あんまりデバッグしてないので対応してないサブコマンドありそうですが・・)
例えば、以下のように鍵の作成が可能です。

> wandbox-ssl genrsa -out test.key 2048
> wandbox-ssl rsa -in test.key -pubout -out test.key.pub

需要があるのかわからないけど、openssl がインストールされてなくても python があれば openssl 相当のことができるようになりました。 

最後に

筆者が明るくない言語はファイルリストの自動登録機能が正しく動作してなかったり、対応してない構文があったりするかもしれません。
(実際、JavaScript/TypeScript の require は対応してないです)
ぜひ、PR いただけると嬉しいです。

ではでは。

2020年6月29日月曜日

Wandbox の CLI ツールを作ってみた


タイトルの通りコマンドラインツールを作ってみました。
リポジトリはこちら。https://github.com/srz-zumix/wandbox-api/

言語は Python で PyPI に Publish してますので、 pip install wandbox-api ですぐに使えます!
PyPI に初めて Publish しました〜
この辺は「Github ActionsでPyPIにパッケージを公開する」を参考に、GitHub Actions で tag をトリガーに Publish されるようにしました。
name: PyPI Publish
on:
  push:
    tags:
      - v*

jobs:
  pypi:
    runs-on: ubuntu-18.04
    steps:
    - uses: actions/checkout@master
    - name: Set up Python 3.7
      uses: actions/setup-python@v1
      with:
        python-version: 3.7
    - name: Init .pypirc
      env:
        PYPI_USERNAME: __token__
        PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
      run: |
        echo -e '[pypi]' >> ~/.pypirc
        echo -e "username = ${PYPI_USERNAME}" >> ~/.pypirc
        echo -e "password = ${PYPI_PASSWORD}" >> ~/.pypirc
    - name: Publish
      run: |
        pip install twine wheel
        python setup.py sdist bdist_wheel
        twine upload --repository pypi dist/*


現在の機能は、Wandbox が対応している言語・コンパイラー・オプションのリストアップとコンパイル&実行です。また、C++ 用に特化した wandbox-cxx コマンドもあります。
使用例
言語リストを取得
$ wandbox lang
Bash script
C
C#
C++
CMake
CPP
CoffeeScript
Crystal
D
Elixir
Erlang
F#
Go
Groovy
Haskell
Java
JavaScript
Lazy K
Lisp
Lua
Nim
OCaml
OpenSSL
PHP
Pascal
Perl
Pony
Python
R
Rill
Ruby
Rust
SQL
Scala
Swift
TypeScript
Vim script
コンパイラーのリストを取得
-l オプションで言語を選択してリストアップします。言語指定がない場合はすべての言語のコンパイラーをリストアップします。
$ wandbox -l C++ compiler
gcc-head
gcc-9.3.0
gcc-9.2.0
gcc-9.1.0
gcc-8.3.0
gcc-8.2.0
gcc-8.1.0
gcc-7.3.0
gcc-7.2.0
gcc-7.1.0
gcc-6.3.0
gcc-6.2.0
gcc-6.1.0
gcc-5.5.0
gcc-5.4.0
gcc-5.3.0
gcc-5.2.0
gcc-5.1.0
gcc-4.9.3
gcc-4.9.2
gcc-4.9.1
gcc-4.9.0
gcc-4.8.5
gcc-4.8.4
gcc-4.8.3
gcc-4.8.2
gcc-4.8.1
gcc-4.7.4
gcc-4.7.3
gcc-4.6.4
gcc-4.5.4
gcc-4.4.7
clang-head
clang-10.0.0
clang-9.0.0
clang-8.0.0
clang-7.0.0
clang-6.0.1
clang-6.0.0
clang-5.0.0
clang-4.0.1
clang-4.0.0
clang-3.9.1
clang-3.8.1
clang-3.7.1
clang-3.6.0
clang-3.5.0
clang-3.4
clang-3.3
clang-3.2
clang-3.1
zapcc-2017.08
zapcc-1.0.1
コンパイラーのオプションを取得
-c オプションでコンパイラーを選択すると、そのコンパイラーで使用できるオプションをリストアップします。指定がない場合はすべてのオプションをリストアップします。

$ wandbox -c clang-head option
C++: 
warning (default)
optimize
cpp-verbose
boost-1.73.0-clang-head (default)
  boost-nothing-clang-head
  boost-1.60.0-clang-head
  boost-1.61.0-clang-head
  boost-1.62.0-clang-head
  boost-1.63.0-clang-head
  boost-1.64.0-clang-head
  boost-1.65.0-clang-head
  boost-1.65.1-clang-head
  boost-1.66.0-clang-head
  boost-1.67.0-clang-head
  boost-1.68.0-clang-head
  boost-1.69.0-clang-head
  boost-1.70.0-clang-head
  boost-1.71.0-clang-head
  boost-1.72.0-clang-head
  boost-1.73.0-clang-head
sprout
msgpack
gnu++2a (default)
  std-c++-default
  c++98
  gnu++98
  c++11
  gnu++11
  c++14
  gnu++14
  c++17
  gnu++17
  c++2a
  gnu++2a
cpp-no-pedantic (default)
  cpp-no-pedantic
  cpp-pedantic
  cpp-pedantic-errors
C++ のコードをビルド・実行する
wandbox-api パッケージには C++ 用のコマンドとして、wandbox-cxx と wandbox-g++/wandbox-clang++ が含まれています。
cxx は wandbox コマンドに -l C++ を指定したのと同等です。wandbox-g++/wandbox-clang++ はさらに -c オプションでコンパイラーの指定をしたものになります
(バージョンはそれぞれ head)
また、これらは通常の wandbox コマンドのコマンドラインオプションに加えて、 C++ コンパイラー向けの Wandbox オプションがコマンドラインオプションで設定できるようになっています。
  --std VERSION         set --std options
  --boost VERSION       set boost options version X.XX or nothing
  --optimize            use optimization
  --cpp-verbose         use cpp-verbose
  --sprout              use sprout
  --msgpack             use msgpack
run に続く引数は、コンパイラーへの引数としてそのまま渡されます。
$ wandbox-clang++ run main.cpp test.cpp
program_message:
Hello, Wandbox!
test
$ wandbox-clang++ run main.cpp test.cpp -DTEST
program_message:
test
test
-s オプションを付けると permlink を発行できます。
wandbox-clang++ -s run main.cpp test.cpp -DTEST
program_message:
test
test


permlink: TuvMXWckFrkxGPAs
url: https://wandbox.org/permlink/TuvMXWckFrkxGPAs
[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ https://wandbox.org/permlink/6AGrKCojBOo1oCUv


使用したソースコードはこちらです。
今後
今後、ちまちまと他の言語のコマンドも追加していこうと思います。
もちろん PR 大歓迎です!
今回は以上です。では。



2020年6月23日火曜日

[AppVeyor] Python3 系を使うようにする

AppVeyor で現在提供されているエージェント(環境)は Python 2.7 がデフォルトになっているので、Python 3 系に変更しました。
AppVeyor の環境にプリインストールされている Python は下記から確認できます。

あとはお好きなバージョンのディレクトリを PATH に追加したら終わりです。

version: 1.0.{build}
branches:
  only:
  - master
image: Visual Studio 2019
init:
- cmd: SET PATH=C:\Python38;C:\Python38\Scripts;%PATH%
以上です。

2017年8月8日火曜日

[CI] Scrutinizer 始めました



今回紹介する CI サービスは Scrutinizer です。
Pricing をみるとフリープランはないように見えますが、OSS であればフリーで使えるようです

今回、使い始めてから紹介できるようになるまでにかなーりの年月を費やしてしまいましたので、Sign up まわりは省略させていただきます。
(C++非サポート&Python のテスト周りで手間取った)

Scruntinizer の魅力
Scruntinizer の魅力は登録するだけですぐに静的解析の結果が得られるところです。
Scruntinizer は静的解析などの検証に特化した CI サービスで、めんどうな設定や YAML を書かずにすぐに始められます。テストもリポジトリを探索して自動で実行してくれます。

あと、レポートもグラフィカルでいい感じです。


使用例
いつもであれば、iutest の使用例で紹介するのですが、C++ は非サポートなので、
今回は「ブログズミ: ソースコード中の単語からの略語/スペルミス検出に挑戦」で作成した
taggertool リポジトリで紹介します。

リポジトリ登録
まずは、リポジトリの登録から始めます。
「Add Repository」ボタンを押すと、以下のような画面になるので、CI したいリポジトリを入力、言語を選択して、登録します。


リポジトリは、Github/Bitbucket/GitLab/PlainGit から選択できます。
Plain Git が可能なので、独自リポジトリが使えるのは良いところだと思います。

登録は以上です。
完了すると最初のテストが実行されます。

taggertool の結果
taggertool の結果はこんな感じです。

サマリー


Issues に、各静的解析で出た結果が出ます。


Code にはクラスおよび関数のサイズと複雑度がリストアップされます。


Inspections はキックされたジョブが並びます。


Reports には最後に実行されたジョブの結果が表示されます。


最後に、Statistics には Quality Rating や issue などの推移がグラフで確認できます。
Quality Rating は上の図で 9 となっているやつで、コードの複雑度、結合度など複数の要素から1つのスコアにしたもので、10点満点で評価されます。


チェック項目を増やす
さて、Scrutinizer には様々なツールで様々な解析ができる準備がされていますが、デフォルトでは多くのチェックは無効になっています。
チェックを有効にするには、Settings の Configuration に Checks ボタンがあるので、そこを開きます。


開くと Not Enabled なチェック項目がリストアップされているので、
必要なものにチェックを入れてください。


チェックの有効化は Congiguration の Repogitry Config や yml に直接書くこともできます。
チェックしたい項目が多い場合は、(UI でチェックするのが面倒なので)こちらの方がよいでしょう。

また、グローバルな設定も作成できるので、
複数プロジェクトで同じチェックをさせたい場合は、そちらを使うと良さそうです。
checks:
    python:
        code_rating: true
        duplicate_code: true
        variables_used_before_assignment: true
        variables_unused_wildcard_import: true
        variables_unused_variable: true
        variables_unused_import: true
        variables_unused_argument: true
        variables_unpacking_non_sequence: true
        variables_undefined_variable: true
        variables_undefined_loop_variable: true
        variables_undefined_all_variable: true
        variables_unbalanced_tuple_unpacking: true
        variables_redefined_outer_name: true
        variables_redefined_builtin: true
        variables_redefine_in_handler: true
        variables_no_name_in_module: true
        variables_invalid_all_object: true
        variables_global_variable_undefined: true
        variables_global_variable_not_assigned: true
        variables_global_statement: true
        variables_global_at_module_level: true
        typecheck_unexpected_keyword_arg: true
        typecheck_too_many_function_args: true
        typecheck_redundant_keyword_arg: true
        typecheck_not_callable: true
        typecheck_no_value_for_parameter: true
        typecheck_no_member: true
        typecheck_missing_kwoa: true
        typecheck_maybe_no_member: true
        typecheck_duplicate_keyword_arg: true
        typecheck_assignment_from_none: true
        typecheck_assignment_from_no_return: true
        string_truncated_format_string: true
        string_unused_format_string_key: true
        string_too_many_format_args: true
        string_too_few_format_args: true
        string_mixed_format_string: true
        string_missing_format_string_key: true
        string_format_needs_mapping: true
        string_constant_anomalous_unicode_escape_in_string: true
        string_bad_str_strip_call: true
        string_constant_anomalous_backslash_in_string: true
        string_bad_format_string_key: true
        string_bad_format_character: true
        open_mode_bad_open_mode: true
        miscellaneous_fixme: true
        newstyle_bad_super_call: true
        logging_unsupported_format: true
        logging_too_many_args: true
        logging_too_few_args: true
        logging_not_lazy: true
        logging_format_truncated: true
        imports_wildcard_import: true
        imports_relative_import: true
        imports_reimported: true
        imports_import_self: true
        imports_import_error: true
        imports_deprecated_module: true
        imports_cyclic_import: true
        format_unnecessary_semicolon: true
        format_trailing_whitespace: true
        format_superfluous_parens: true
        format_old_ne_operator: true
        format_multiple_statements: true
        format_mixed_indentation: true
        format_missing_final_newline: true
        format_lowercase_l_suffix: true
        format_line_too_long:
            max_length: '100'
        format_bad_whitespace: true
        format_bad_indentation:
            indentation: '4 spaces'
        format_backtick: true
        exceptions_raising_non_exception: true
        exceptions_raising_string: true
        exceptions_raising_bad_type: true
        exceptions_pointless_except: true
        exceptions_notimplemented_raised: true
        exceptions_catching_non_exception: true
        exceptions_broad_except: true
        exceptions_binary_op_exception: true
        exceptions_bare_except: true
        exceptions_bad_except_order: true
        design_interface_not_implemented: true
        design_abstract_class_not_used: true
        design_abstract_class_little_used: true
        classes_valid_slots: true
        classes_super_init_not_called: true
        classes_signature_differs: true
        classes_protected_access: true
        classes_non_parent_init_called: true
        classes_non_iterator_returned: true
        classes_no_self_use: true
        classes_no_self_argument: true
        classes_no_method_argument: true
        classes_no_init: true
        classes_missing_interface_method: true
        classes_method_hidden: true
        classes_interface_is_not_class: true
        classes_bad_staticmethod_argument: true
        classes_bad_mcs_method_argument: true
        classes_bad_mcs_classmethod_argument: true
        classes_bad_context_manager: true
        classes_bad_classmethod_argument: true
        classes_attribute_defined_outside_init: true
        classes_arguments_differ: true
        classes_access_member_before_definition: true
        classes_abstract_method: true
        basic_yield_outside_function: true
        basic_useless_else_on_loop: true
        basic_unreachable: true
        basic_unnecessary_pass: true
        basic_unnecessary_lambda: true
        basic_star_args: true
        basic_return_outside_function: true
        basic_return_in_init: true
        basic_return_arg_in_generator: true
        basic_pointless_string_statement: true
        basic_pointless_statement: true
        basic_old_raise_syntax: true
        basic_not_in_loop: true
        basic_nonexistent_operator: true
        basic_missing_reversed_argument: true
        basic_missing_module_attribute: true
        basic_missing_docstring: true
        basic_lost_exception: true
        basic_init_is_generator: true
        basic_function_redefined: true
        basic_expression_not_assigned: true
        basic_exec_used: true
        basic_eval_used: true
        basic_empty_docstring: true
        basic_duplicate_key: true
        basic_duplicate_argument_name: true
        basic_dangerous_default_value: true
        basic_bad_reversed_sequence: true
        basic_assert_on_tuple: true
        basic_abstract_class_instantiated: true

最後に
Scruntinizer は、最初から静的解析ツールが用意されており、それを目的にするのであれば、
通常の CI サービスよりも、とても簡単に恩恵が得られるのでオススメです。
(C++ がサポートされるともっとステキですが…あ、あと Scruntinizer のスペルが全然覚えられない…

今回は Python で利用しましたが、他の言語で何か作るときは、また利用したいなと思います。

以上。


2017年7月31日月曜日

続・ソースコード中の単語からの略語/スペルミス検出に挑戦

ブログズミ: ソースコード中の単語からの略語/スペルミス検出に挑戦」で紹介した略語チェッカーを大幅に更新したので、続報です。
ついでなので、Glosbe でリクエスト制限にかかってしまった場合の対処方法も紹介しておきます。

変更点
usage: abbreviation.py [-h] [-v] [-g FILE] [-w FILE] [-e EXCLUDE]
                       [-a ABBREVIATION] [--glosbe] [--dejizo] [--cache]
                       [--load-cache NAME] [--cache-dir DIR]
                       [--disable-keywords] [-x {c++,c#,objc,diff}]
                       [--list-all] [--progress] [--safe-mode]
                       [--encoding ENCODING] [--extension EXTENSION]
                       [--ignore-noexists] [--relpath] [--word WORD] [-]
                       [FILE/DIR [FILE/DIR ...]]

positional arguments:
  FILE/DIR              source code file/dir

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit
  -g FILE, --gene FILE  exlude word
  -w FILE, --whitelist FILE
                        whitelist file
  -e EXCLUDE, --exclude EXCLUDE
                        exlude word
  -a ABBREVIATION, --abbreviation ABBREVIATION
                        abbreviation word
  --glosbe              use online translation service (glosbe)
  --dejizo              use online service (dejizo)
  --cache               online translation cache enable
  --load-cache NAME     load translation cache
  --cache-dir DIR       translation cache directory
  --disable-keywords    disable general and language keywords
  -x {c++,c#,objc,diff}, --language {c++,c#,objc,diff}
                        select language
  --list-all            list up all location
  --progress            print percent progress
  --safe-mode           api request limit safe mode(glosbe)
  --encoding ENCODING   set file encoding
  --extension EXTENSION
                        file extension matcher
  --ignore-noexists     ignore option file not exists
  --relpath             print relative path
  --word WORD           dircet check words
  -                     source code from stdin

diff ファイルに対応
便利になった機能の1つがこれだと思います。
diff ファイルから差分があったところだけをチェックするので、コードレビューのときに重宝します。
対応しているフォーマットは Unified format になります。

略語判定ロジックの大幅更新
「~の略語」だけでなく、「~の過去形」「~の複数形」なども検出して、辞書判定するように改善しました。主に、boost のヘッダーファイルをチェッカーに入力して、その結果を見ながら、期待に沿わないものに対応していった感じです。

ただ、ソースコード中で出てきた場合の一般的な意味と、辞書に載っている意味で剥離している場合もあります。
例えば、「apis」とあったら、「APIs」のことだとプログラマーは思うと思うのですが、辞書的には「蜂」です。
こういったものを機械的に振り分けるのは(今の自分の力量では)難しかったので、100% プログラマーが期待する結果になるわけではありませんので、ご注意!
ディープラーニングさせたらいいのかな?

ともあれ、boost を解析して自分なりに納得のいくところまで対応できたかなーと思ってます。

Glosbe でリクエスト制限にかかってしまった場合の対処方法
('Http error:', u'429 Client Error:  for url: https://glosbe.com/gapi/translate?dest=ja&phrase=opendir&from=en&pretty=true&format=json')
('request count: ', 802)
Please access the glosbe, click the search button and check reCAPTCHA.
Glosbe でリクエスト制限にかかると上記ログが出力されます。
これが出た場合、以下の方法で回復が可能です。

まずは、https://ja.glosbe.com/ にアクセスして、普通に辞書検索します。


以下のような画面になるので、チェックをクリアすると制限が解除されます。
(何回も制限かけられましたが、今のところこの方法ですぐに解除できています。)


最後に
このツールを実際に使ってみて、当初の目的の略語検出よりも typo 検出の方が役に立ちました。
実際にヒドイ typo が見つかりました…
https://github.com/srz-zumix/iutest/commit/c063225f5abfeb157f671e3fc55b779f32e12193

ただ、typo チェックであれば、「Pull Requestに潜むタイポを自動的に検出し、修正を代行するBot - Qiita」の方がよさそうだなーと思ったのと、PyEnchant ってのがあるほを初めて知ったので機会があれば使ってみようと思いました。

最後ですが、今回のアップデートで自己満足できるところまで作れたので、これで開発を一旦終了しようと思います。
(要望いただければ対応します)
ではでは。

2017年3月28日火曜日

[自動化] ニコニコ静画で削除されたクリップを通知

クリップしたものの削除されてしまった投稿は、クリップから削除するようにしているが、全クリップ・全ページを目視するのは面倒なので、自動化しました。

環境は Semaphore + Python + BeautifulSoup で、ソースコードはこちらです。
https://github.com/srz-zumix/niconico-seiga

Semaphore では定期実行が可能なので、こちらを利用しました。
毎日チェックして削除された投稿があるとメール通知が飛んできます。


自動削除までやったらもっと楽になりますが、
これでも、どのクリップのどのページの投稿が消えたかすぐわかるので、随分楽になります。
自動化素晴らしい!
今回は以上です。では。

2016年11月30日水曜日

[iutest] iutest.min.hpp の圧縮

※下書きしたまま公開するのを忘れてました。
iutest v1.15.2 が既にリリースされていますが、以下は v1.15.1 のときの結果になります。ご了承ください。



iutest v1.15.0 のときには、824,858 byte ありましたが、これをさらにコンパクトにして 573,897 byte にまで小さくしました。
こんな感じだったのが、
namespace iutest
{
class TestInfo
: public detail::iu_list_node<TestInfo>
{
public:
TestInfo(detail::iuITestCaseMediator* testcase,const char* name,detail::iuFactoryBase* factory)
: m_testname(name)
,m_factory(factory)
,m_testcase(testcase)
,m_should_run(true)
,m_ran(false)
,m_disable(false)
,m_skip(false)
,m_matches_filter(true)
{
m_mediator.SetPointer(this);
if( detail::IsStringForwardMatching(name, "DISABLED_")
|| (strstr(name, "/DISABLED_") != NULL) )
{
m_disable = true;
}
}
public:
const char* test_case_name(void) const { return m_testcase->test_case_name(); }
こんな感じになりました。
namespace iutest{class TestInfo: public detail::iu_list_node<TestInfo>{public: TestInfo(detail::iuITestCaseMediator* testcase,const char* name,detail::iuFactoryBase* factory): m_testname(name),m_factory(factory),m_testcase(testcase),m_should_run(true),m_ran(false),m_disable(false),m_skip(false),m_matches_filter(true){m_mediator.SetPointer(this);if(detail::IsStringForwardMatching(name,"DISABLED_")||(strstr(name,"/DISABLED_")!=NULL)){m_disable=true;}}public: const char* test_case_name()const{return m_testcase->test_case_name();}const char* name()const{return m_testname.c_str();}bool should_run()const IUTEST_CXX_NOEXCEPT_SPEC{return m_should_run;}bool is_ran()const IUTEST_CXX_NOEXCEPT_SPEC{return m_ran;}bool is_disabled_test()const IUTEST_CXX_NOEXCEPT_SPEC{return m_disable;}bool is_skipped()const IUTEST_CXX_NOEXCEPT_SPEC{return m_skip||m_test_result.Skipped();}bool is_reportable()const IUTEST_CXX_NOEXCEPT_SPEC{return m_matches_filter;}TimeInMillisec elapsed_time()const{return m_test_result.elapsed_time();}const TestResult* result()const IUTEST_CXX_NOEXCEPT_SPEC{return &m_test_result;}const char* value_param()const{return m_value_param.empty()?NULL:m_value_param.c_str();}const char* type_param()const{return m_testcase->type_param();}::std::string testcase_name_with_default_package_name()const{return TestEnv::AddDefaultPackageName(test_case_name());}public: bool HasFatalFailure()const{return m_test_result.HasFatalFailure();}bool HasNonfatalFailure()const{return m_test_result.HasNonfatalFailure();}bool HasFailure()const{return m_test_result.Failed();}bool Passed()const{if(is_skipped()){return false;}return m_test_result.Passed();}public: ::std::string test_full_name()const{::std::string fullname=test_case_name();fullname+=".";fullname+=name();return fullname;}::std::string test_name_with_where()const{::std::string str=m_testname;if(value_param()!=NULL){str+=", where GetParam() = ";str+=m_value_param;}return str;}public: static bool ValidateTestPropertyName(const ::std::string& name){const char* ban[] ={"name","status","time","classname","type_param","value_param"};
大きな変更は、同じヘッダーが2つ分含まれていたバグの修正と改行コード、空白の削減をしています。
iutest.min.hpp は Wandbox や paiza.IO のようなオンラインコンパイラーを使うときに使用をしていますので、サイズが小さくなることにメリットはあるわけです。

paiza.IO
さて、paiza.IO での実行は以前も記事に書いたように Too long 、ソースコードサイズが大きすぎて実行できませんでした。
ブログズミ: paiza.IO の API を使ってみた

iutest.min.hpp のサイズが小さくなりましたが、paiza.IO の制限 10,000 byte からはまだまだ大きすぎます。
そこで、paiza.IO 向けに更に削減するようにしてみました。
paiza.IO はコンパイラーが決まっているので、ある程度プリプロセスをかけるようにした感じです。

具体的には __clang__ や __has_include, __has_feature などをプリプロセスして、#if を削減しています。
また、Google Test 互換用のヘッダーファイルを除外するようにしています。

これで、テストコードを含めて 388,831 byte まで減らすことができました。
けど、まだまだ足りませんね。。。

ただ、API では無理ですが、ブラウザからは実行できるくらいに収まったようです。


まだ API からは実行できませんが、ここまででキリにしたいと思います。
それでは。

2016年11月22日火曜日

ソースコード中の単語からの略語/スペルミス検出に挑戦

2018/2/22 追記:続編書いた「続・ソースコード中の単語からの略語/スペルミス検出に挑戦」。Glosbe の制限の解決策も書いてあります。

ブログズミ: TreeTagger を使ってソースコード中の単語をリストアップしてみた」の続きです。
今回は抽出した単語が略語、もしくはスペルミスなどがないかチェックします。

なぜ、こんなものを作ったのか?それは


ということです。

今回作成したものはそのうちの一つで、「許可された略語以外は禁止」という規約に対して、その発見を"補助"するものです。
(あくまで補助でこれをかけて何も検出されなかったら OK というわけではありません。でも、人間が一語一語確認するものでもないので、これでも十分かなぁーっと思ってます。)

できたもの
最初に出来上がったものを紹介したいと思います。
ソースコードはこちらで公開しています。

usage: abbreviation.py [-h] [-v] [-g FILE] [-w FILE] [-e EXCLUDE]
                       [-a ABBREVIATION] [--glosbe] [--dejizo] [--cache]
                       [--load-cache NAME] [--cache-dir DIR] [--list-all]
                       FILE/DIR [FILE/DIR ...]

positional arguments:
  FILE/DIR              source code file/dir

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit
  -g FILE, --gene FILE  exlude word
  -w FILE, --whitelist FILE
                        whitelist file
  -e EXCLUDE, --exclude EXCLUDE
                        exlude word
  -a ABBREVIATION, --abbreviation ABBREVIATION
                        abbreviation word
  --glosbe              use online translation service (glosbe)
  --dejizo              use online service (dejizo)
  --cache               online translation cache enable
  --load-cache NAME     load translation cache
  --cache-dir DIR       translation cache directory
  --list-all            list up all location

試しに iutest のソースコードを調べてみました。


設計としては、抽出した単語を辞書(オンライン/オフライン)から検索して、辞書にあれば問題なし(辞書に略語として登録されている場合は×)、辞書になかった場合に疑わしい単語として警告する設計です。

以下で詳しく説明していきます。

単語の抽出
「ソースコード中の単語」の抽出は libclang などを利用してやるほうが正しいとは思うのですが、
ブログズミ: TreeTagger を使ってソースコード中の単語をリストアップしてみた」でやったように形態素解析ツールの TreeTagger を使ってやりました。


オンライン辞書サービスを利用する
Glosbe
Glosbe Api
英単語の日本語訳をプログラム内やターミナル内で取得する - roombaの日記

いいね~いいね~とコーディングしていたら、いつの間にかエラーに…
Too many queries, your IP has been blocked

    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 429 Client Error:  for url: https://glosbe.com/gapi/translate?dest=ja&phrase=incg&from=en&pretty=true&format=json
というわけで、制限を超えてしまったようです…

ドキュメントによれば、「ブロックするリクエスト数は明確には決まってないけど、人間じゃなくてロボットだと判断したらブロックするよ」と書いてあります。

ロボットなのでしょうがないですね…
開発者でブロックされたら連絡してねとありますが、別のサービスを探すことにしました。
(一応、1日経つとブロック解除されています。今のところ)

WORDS API
WORDS API
無料プランでも 2,500 Requests/Day でよさそうだったんですが、クレジットの登録が必要だったので今回は保留としました。
(V-プリカとか使えば良いんですけど、めんどくさいので…)
(制限オーバーしたら請求が発生する感じに見えたのも保留した理由の1つ…)

デ辞蔵
デ辞蔵 オンラインでもオフラインでも使える電子辞書
続いて目をつけたのがこちら。
(SOAP ってなんだろ?って思ったんですが、そういうのがあったんですね)

こちらは特に制限がないのですが、注意事項として
・短時間にアクセスが集中するような呼び方(1秒間に20回以上の呼び出しなど)
・定常的に一定のアクセスが続くような呼び方(数秒ごとに自動的に呼び出すなど)
とありました。→「BizPal - デ辞蔵Tech よくある質問と回答
あと無償版は動作保証なし。

英和辞書で使えるのは、EDICT和英辞典(EJdict) と 三省堂 デイリーコンサイス英和辞典 試用版(DailyEJL)の2つあったので、両方を検索するようにしてます。ただし、 Glosbe よりも辞書の単語数は少ない印象
秒間20リクエストは場合によっては超えてるかもしれないので、ゴメンナサイm(__)m

その他
この記事も早く公開したいし、組み込みはこの辺で一旦終わりにしておきます。
その他の辞書サービスがあれば使ってみたいと思います。
あとは、翻訳サービス使う方法もありなのかも。

オンライン辞書のヒット結果をキャッシュする
さて、オンライン辞書から単語を引いてくることができるようになりましたが、リクエスト制限のことを考えなければいけません。というわけで、なるべくリクエストを投げないようにするために、一度検索ヒットした単語をローカルにキャッシュする仕組みを入れました。

今回 iutest を解析したときのキャッシュがリポジトリに入ってます。
このキャッシュはサービスごとに記録し、どんどん追記していく仕組みです。

また、キャッシュは2種類あります。
辞書には「略語」として単語登録されているものもありますので、ヒットしたけど略語だった場合はブラックリストに登録しています。
(※この略語判定が大変だった。特に EDICT は統一性がないのでかなり妥協…)

オフライン辞書を利用する
ソースコード中の単語で、外部に出したくないものもあると思います。(コードネームとか製品名とか?)
そこで、オフラインの辞書にも対応しています。

辞書は3つのタイプが利用可能です。

--whitelist FILE
単純な形式で単語を1行ずつ読み込みます。

--gene FILE
オープンしたファイルから、 '^[a-zA-Z][a-z]+$' にマッチするものを単語として読み込みます。
(これは GENE95 を想定して用意しているので、それ以外には使わないかもしれませんが…)

--cache-load XXX
オンライン辞書でキャッシュしたファイルを読み込みます。
XXX の部分にはオンライン辞書のオプション名を入れます。(現状、glosbe と dejizo のみ対応)
形式はホワイトリスト+ブラックリストになっています。

オンライン辞書では、場合によって "ver" などが "version" の略語として単語登録されている場合がありますので、
そのような単語はブラックリストとしてキャッシュしています。
(ホワイトリストの形式 --whitelist で読み込めるファイルと同じですので、これだけを --whitelist で読み込むことも可能です。)


使ってみた結果
当初は略語の検出を目的に作成しましたが、どちらかと言うと typo 、誤字脱字の方がよく検出される結果となりました。
(恥ずかしいのでちゃんと直しました。 > https://github.com/srz-zumix/iutest/commit/6c767630c810ef27b92bfca856a657209945433c

使ってみたい方、github で公開してますのでご自由にお使いください。




2016年10月11日火曜日

TreeTagger を使ってソースコード中の単語をリストアップしてみた

ちょっと入用で、ソースコード中の単語をリストアップしてみました。
お仕事で MeCab を使って日本語テキストを解析して、「とあること」の検出ができないかなーと試したことがあったのですが、今回はソースコードの単語を抽出して「とあること」をしようと思いついたので、やってみました。

ソースコードからの単語検出ということで、英語の形態素解析ツールを使うことにしました。
今回使ったのは TreeTagger です。
環境は Windows + TreeTagger + Python です。



TreeTagger のインストール
WIndows にインストールする場合の手順はこちら
tree-tagger-windows-X.X.zip (筆者ダウンロード時は 3.2)をダウンロードしたら展開し、INSTALL.txt に従ってインストールします。
要約すると、Perl のインストールと、必要なパラメーターファイルを .par 拡張子に変えて lib に置くことと、C:\TreeTagger に配置して bin フォルダを PATH に通せば OK。
※ C:\TreeTagger なのは bat ファイルにパスがベタ書きされているため。別にここである必要はないです。
※ Perl も今回は Python から呼び出すのでなくても問題ない

今回、言語は英語を使います。こちらからパラメーターファイル(English parameter file)をダウンロードしました。
http://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/#Linux

TreeTagger を Python から使う
検索して一番最初に出てきた treetaggerwrapper を使用することにしました。
最初に書いた参考URLではダウンロードしてインストールしていましたが、pip からインストールできるようだったのでそちらでインストールしました。
pip install treetaggerwrapper
pip install six

※ six は treetaggerwrapper が使ってるので、なければインストール

treetaggerwrapper.py は english.par を要求するので、TreeTagger のインストールでダウンロードしてきたパラメーターファイルをコピーリネームしておきます。

試しに以下のサンプルを実行します。
import treetaggerwrapper
import os

tagdir = os.getenv('TREETAGGER_ROOT')
tagger = treetaggerwrapper.TreeTagger(TAGLANG='en',TAGDIR=tagdir)
tags = tagger.TagText(u"Save the time of the reader. SAMPLETEST. SampleTest.")
for tag in tags:
    print tag

TAGDIR には TreeTagger のインストールディレクトリを指定しますが、bin に PATH が通っていれば空で問題ないようです。

ソースコード中の単語のリストアップ
とりあえず、コメント行(// で始まる行、/**/ や行末コメントは非対応)除いてリストアップしてみました。
対象のソースコードは iutest の実装全部入り(fused-src/iutest.hpp)です。


C++ のキーワードを除外するとこんな感じ。


圧倒的に iutest が多いですね。(当たり前か)

リストアップに使用したソースコードは github で公開しています。
https://github.com/srz-zumix/taggertool

2016年8月2日火曜日

[C++] Shippable + Wandbox でテストしてテスト結果の集計までを行う

ブログズミ: Shippable + Wandbox で C++ の CI 環境構築
以前、上記記事で Shippable + Wandbox で C++ のテスト実行をするようにしたのですが、iutest v1.15.2(v1.15.1) で Wandbox でのテスト結果を xml 出力できるように対応したので、テスト結果の集計まで行うようにしました。

iutest + Wandbox
iutest の Wandbox 実行ツールを利用することで、iutest で記述されたテストコードを Wandbox 上で実行できます。iutest v1.15.2(v1.51.1) では、このツールに xml 出力機能が追加されました。
iuwandbox.py test.cpp --xml test_result.xml
--xml コマンドラインオプションを付けると、指定のパスにテスト結果の xml を出力します。

Shippable でテスト結果 xml の集計
Shippable では junit 形式のテストレポートが集計できます。
Shippable Build Configuration - Shippable Documentation

方法としては簡単です。
shippable/testresults フォルダを作成して、そのフォルダに xml ファイルをコピーするだけです。

e.g.
build:
  ci:
    - mkdir -p shippable/testresults
    - nosetests python/sample.py --with-xunit --xunit-file=shippable/testresults/nosetests.xml
詳しくはドキュメントを確認してください。
iutest の場合
iutest では以下のようにテストをしています。
(Shippable だけでなく Wercker + Wandbox でも同様の方法でテストを実行しています。)
language: python

python: 2.7
cache: true
parallelized_test: true

branches:
  except:
    - gh-pages

install:
  - pip install requests

before_script:
  - export PYTHONDONTWRITEBYTECODE=1
  - make -C tools/fused
  - mkdir -p shippable/testresults

script:
  - cd tools/wandbox
  - python ./iuwandbox.py --list_options ${WANDBOX_COMPILER}
  - python ./iuwandbox.py ../../test/syntax_tests.cpp -c ${WANDBOX_COMPILER} -f"-DIUTEST_USE_MAIN=1" --default --boost nothing --encoding utf-8-sig --expand_include --verbose --xml ../../shippable/testresults/test_result.xml

env:
  matrix:
    - WANDBOX_COMPILER=gcc-head
    - WANDBOX_COMPILER=gcc-6.1.0
    - WANDBOX_COMPILER=gcc-5.3.0
    - WANDBOX_COMPILER=gcc-5.2.0 # travis (5.2.1)
#    - WANDBOX_COMPILER=gcc-5.1.0 # wercker
#    - WANDBOX_COMPILER=gcc-4.9.2 # circle ci
#    - WANDBOX_COMPILER=gcc-4.9.1 # wercker
#    - WANDBOX_COMPILER=gcc-4.9.0 # wercker
#    - WANDBOX_COMPILER=gcc-4.8.2 # wercker
    - WANDBOX_COMPILER=gcc-4.8.1
    - WANDBOX_COMPILER=gcc-4.7.3
#    - WANDBOX_COMPILER=gcc-4.6.4 # drone
    - WANDBOX_COMPILER=gcc-4.5.4
#    - WANDBOX_COMPILER=gcc-4.4.7 # snap ci
    - WANDBOX_COMPILER=gcc-4.3.6
    - WANDBOX_COMPILER=clang-head
#    - WANDBOX_COMPILER=clang-3.8 # travis ci
#    - WANDBOX_COMPILER=clang-3.7 # circle ci / wercker
#    - WANDBOX_COMPILER=clang-3.6 # semaphore
#    - WANDBOX_COMPILER=clang-3.5 # c++config.h not found
    - WANDBOX_COMPILER=clang-3.4
#    - WANDBOX_COMPILER=clang-3.3 # wercker
#    - WANDBOX_COMPILER=clang-3.2 # wercker
#    - WANDBOX_COMPILER=clang-3.1 # wercker
    - WANDBOX_COMPILER=clang-3.0

after_success:
  - echo OK
  
notifications:
  email:
    on_success: change
    on_failure: always



最後に
Wandbox でのテスト結果を xml ファイルに出力できるようになったことで、テストツールとして完成されてきた感じがしてきました。
iutest のテストとしてはかなり便利に使わせていただいています。
もう個人的には最強の環境になったかなと思います。

ただ、複数ファイルのコンパイルができない(ヘッダーは可能)ので、まだちょっとプロダクトのテストをするとまでは至っていませんが、ヘッダーオンリーなライブラリーとかであれば、かなり良いテスト環境になるのではないでしょうか。

2016年4月25日月曜日

SideCI 始めました

SideCI は他の CI サービスと異なり、コードレビューを自動化するコードレビュー支援サービスです。
ついこの間、資金調達のニュースが出てましたね。
そして、βから正式版になりました。
SideCIは2016年4月より「正式版」になります - SideCI Blog


実は前々から知ってはいたのですが、対応している言語に C++ がないので敬遠してました。
ただ、1回使い勝手とかを知っておきたいと思ったので、今回試しに使ってみました。

iutest で試す
iutest は C++ Testing Framework です。
SideCI は C++ 対応してないので、C++ のコードレビューはできませんが、iutest には Python ツールもあるので、そちらをレビューするようにしました。
ちなみに、オープンソースであれば SideCI は無料で利用できます。 料金プラン

まずは、サインアップしたらリポジトリ登録をします。
登録までは4ステップ。

1. アカウントの選択

2. リポジトリの選択

3. 解析ツールの選択

4. 完了


これで登録が完了しました。
ただ、これだけではまだ解析はされません。
SideCI は Pull Request があったときに解析される仕組みです。それ以外はマニュアル実行する必要がありますので、とりあえずそれで試してみましょう。

iutest での flake8 の結果。


感想
  • Manual 実行する必要がある(Pull Request は自動っぽいけど)
    push したときも自動で解析かけてほしい。(コメントはいらない)
  • 項目が見やすくなっているのは良い

    iutest は Codeship で Cpplint をかけてますが、ログ出力を見るよりも見やすくて良いと思いました。
  • C++ 対応して欲しい
    Cpplint にも対応して欲しいです。
    ブログズミ: cpplint でコーディングチェック
  • Badge はないのかな?
あとは、気になる挙動がちらほら…

記事書くために、リポジトリの Disable してもう一度登録しようと思ったら、リポジトリの選択で選択しても反応しなくなってしまいました。Beta のときは iutest の登録ができていたのですが…正式リリースしてからか?それとも Disable したからか?
エラー内容は「Uncaught TypeError: Cannot read property 'toLowerCase' of null」
(Python で書いてるリポジトリは登録できた。アイコンが出てないリポジトリが登録できないっぽい)


他にもなんか挙動がおかしいなと思うことがあったので、本格的に使うのはまだまだ先かな…
(消えた iutest プロジェクトがなぜか見えたり、他のリポジトリを有効にすると見えなくなったり…よくわからん…)

なにんせよ、 Cpplint が欲しいです。

あと、SideCI は国産なのが特徴の1つです。
とりあえず、お問い合わせしてみようと思います。
ではでは。

2016/04/26 追記:
対応していただきました。現在は登録できます。
ただし、一度 Disable にしたリポジトリは別のエラーが出てダメでした。
2016/04/27 追記:
C++ のリポジトリでエラーになっていたようでした。
こちらもご対応頂いたので、現在は問題なく登録できると思います。
SideCI チームの方々ご対応ありがとうございましたm(__)m

2016年1月26日火曜日

[Doxygen] 必ずインクルード的なことをしてみた

Doxygen で必ずインクルード的なオプションってないの?

ないっぽい。

ので、回避策。

INPUT_FILTER を使う
Doxygen のオプションに INPUT_FILTER というものがあり、これを利用して実現しました。

Doxygen 設定
INPUT_FILTER タグでは、各入力ファイルをフィルタリングするのに使うプログラムを指定します。doxygen は、フィルタープログラムを起動するために、次のコマンドを (popen() を介して) 実行します。

<filter> <input-file>
ここで、<filter> は、INPUT_FILTER タグの値です。 また、<input-file> は、入力ファイルの名前です。 doxygen は、その後、フィルタープログラムが標準出力に書き出した出力を使用します。

今回用意したフィルタープログラムは python で以下のように書きました。
import sys

def main():
    f = open(sys.argv[1])
    n = 0
    if f:
        for line in f:
            sys.stdout.write(line)
            if line.find("//=========================================================") != -1:
                n += 1;
                if n == 2:
                    print('#include "iutest_config.hpp"')
    f.close()

if __name__ == '__main__':
    main()

ファイルの先頭にある Doxygen コメントの直後に「必ずインクルードしたいファイル」のインクルードディレクティブを挿入しているだけです。
先頭のコメントを読み飛ばす方法は、もっと汎用性あるやり方にもできますが、今回は iutest でのみ使えればよかったので簡単に済ませました。


必ずインクルードができたことで…
iutest では iutest.hpp ヘッダーファイルをインクルードして使うことを想定しています。
そのため、#include も端折れるところは端折ってます。
なので、include/*.hpp の別のファイルを"それだけで"インクルードしても、#include が足らずに使えなかったりします。

Doxygen はファイル個別に評価されるため、同様に #include が足りない状態で解析がされてしまいます。
そうするとマクロ定義がなかったりして、#if/#endif ブロックが有効にならずにドキュメント化されない、というのが問題になってました。
今回、「必ずインクルード」ができるようになったことで、このようなファイルの解析が期待通りにできるようになりました。


設定ファイルで指定できたら一番いいんですけどね。
今回は以上。

2015年9月24日木曜日

[iutest] コンパイルエラーになることをテストしてみた

iutest v1.13.0 をリリースしましたが、
iutest v1.12.0 あたりからコンパイルエラーになることをテストするようにしていました。

手法としては、コンパイラ出力を解析してエラーが発生したことを確認するシンプルな方法です。
この解析周りも含め Python で書いてます。
https://github.com/srz-zumix/iutest/blob/master/tools/python/iutest_compile_error_test.py

さて、テストですので期待値を書くことになります。
これはソースコードに直接書ける仕組みにしました。

以下のように書きます。
IUTEST(StaticAssertTypeEqTest, Fail)
{
    IUTEST_TEST_COMPILEERROR("static_assert_typeeq")
    ::iutest::StaticAssertTypeEq<float, int>();
}

IUTEST_TEST_COMPILEERROR の引数にはエラーメッセージを記述します。
このマクロの直後の行にエラーがあり、エラーメッセージに引数の内容が含まれているとテスト成功です。
(エラーメッセージはコンパイラーごとに異なるので注意が必要ですが…)

実際にテストするときは、以下のようにします。
g++ -I../include -g -Wall -Wextra   -std=c++1y -Werror=undef -o static_assertion_failure_tests static_assertion_failure_tests.cpp   2>&1 | python ../tools/python/iutest_compile_error_test.py -c g++
コンパイラーの標準エラー出力を iutest_compile_error_test.py に食わせてあげます。今回は g++ の出力なので、 -c オプションで指定します。
対応しているコンパイラーは g++,clang,cl(Visual Studio) の3つです。
(この機能自体をちゃんとテストしていないので対応できてないケースがあるかもしれませんが…)

実行するとテスト結果が出力されます。
g++ -I../include -g -Wall -Wextra   -std=c++1y -Werror=undef -o static_assertion_failure_tests static_assertion_failure_tests.cpp   2>&1 | python ../tools/python/iutest_compile_error_test.py -c g++
[OK] IUTEST_TEST_COMPILEERROR( "static_assert_typeeq" ): static_assertion_failure_tests.cpp: 24
[OK] IUTEST_TEST_COMPILEERROR( "static_assert_typeeq" ): static_assertion_failure_tests.cpp: 29
[OK] IUTEST_TEST_COMPILEERROR( "static_assert" ): static_assertion_failure_tests.cpp: 35
[OK] IUTEST_TEST_COMPILEERROR( "static_assert" ): static_assertion_failure_tests.cpp: 41

失敗した場合はこんな感じ。
[OK] IUTEST_TEST_COMPILEERROR( "static_assert_typeeq" ): static_assertion_failure_tests.cpp: 24
[OK] IUTEST_TEST_COMPILEERROR( "static_assert_typeeq" ): static_assertion_failure_tests.cpp: 29
[OK] IUTEST_TEST_COMPILEERROR( "static_assert" ): static_assertion_failure_tests.cpp: 35
[NG] IUTEST_TEST_COMPILEERROR( "hogehoge" ): static_assertion_failure_tests.cpp: 41

テストが失敗したときのメッセージとかはまだしっかり作りこめてませんが、これでコンパイルエラーになるべきところをテストできるようになりました。
ライブラリとか作ってると、こういうテストをしたくなると思います。
もっとよさげなツールがあれば使っていきたいところですが…

では。

2015年6月8日月曜日

Twilio を使ってみた - その5

「Twilio を使ってみた」シリーズ5回目です。
これまでは"電話"を扱ってきましたが、今回は SMS を使ってみたいと思います。

SMS はトライアルアカウントでは使えません。
残念です。が、私はかなり前にトライアルアカウントが切れたため、アップグレードしてます。
ブログズミ: Twilio の無料トライアル期間が終了
せっかく使えるようになったのだから~、というわけで今回は SMS をやります。

REST API
前回に引き続き、RESET API を使います。
SMS を送る API は、こちら。
Twilio Cloud Communications - APIs for Voice, VoIP, and Text Messaging

以下は、サンプルのソースコードです。
# Download the Python helper library from twilio.com/docs/python/install
from twilio.rest import TwilioRestClient
 
# Your Account Sid and Auth Token from twilio.com/user/account
account_sid = "AC5ef8732a3c49700934481addd5ce1659"
auth_token  = "{{ auth_token }}"
client = TwilioRestClient(account_sid, auth_token)
 
message = client.messages.create(body="Jenny please?! I love you <3 data-blogger-escaped-from_="+14158141829" data-blogger-escaped-media_url="http://www.example.com/hearts.png" data-blogger-escaped-message.sid="" data-blogger-escaped-pre="" data-blogger-escaped-print="" data-blogger-escaped-to="+15558675309">
電話と掛けるときとの違いは、messages.create を使うとこと url 指定ではなく送信したいメッセージを body に直書きするとこです。 あとは、media_rul の指定もできますが今回は扱いません。
SMS の制限

https://twilio.kddi-web.com/function/sms/
050番号ではSMSをご利用になれません。電話番号購入メニューから「アメリカ」の電話番号をご購入の上、ご利用ください(現在、SMS サービスはアメリカ番号のみでの提供です)。
というわけで、050 番号では SMS を使えないので、電話番号を購入しました。
電話番号の購入
電話番号の購入を開くと、まず最初に電話番号の検索ページが出ます。


今回は、SMS を使うのが目的なので、 United States を選びます。
条件に SMS を指定して、検索。


番号がたくさん出てくるので、好きな番号を選んで購入ボタンを押します。
以下のような画面が出るので、「Buy This Number」を押して購入は完了です。


これで、SMS が使える電話番号を手に入れられました。

実装
早速、iutest の twilio 連携ツールに組込みました。
https://github.com/srz-zumix/iutest https://github.com/srz-zumix/iutest/blob/master/tools/twilio/iutwilio.py
sms オプションを使ってテスト結果を SMS で通知できます。