git-助けてというすごく便利なエイリアスを作った - 宇宙線 を読んでいて、むしろ「むしろ日本語で扱えるgitコマンドみたいなものを作ったほうがいいのでは?そのほうがかわいいし、わかりやすいじゃん!」などと思ったので、そういう日本語で使えるgitのやつである「gimawari」を作成しました。
二つの説がある。
ちなみに、日本語を扱うという性質であるため、全角空白も、引数の区切りとして認識するように製作されてある。
本当だったら、git-hogehoge 的にbinを通せば、"git hogehoge"みたいな命令でも通るようになるらしいんだけど、そういう仕様にしなかったのは、普通のgitと混ぜるのは超アレ感あるのと、どちらかといえば、独立した便利ソフトみたいな立ち位置のほうがいいかな~と思ったので、そんな設計にしてある。少々癖が強いアプリケーションであることは間違いないので、独立していたほうが混乱が少なくて済むのかも……みたいな気持ち。
現状として、binの通っているPythonは3ではなく、Python2.7であることを考えたならば、Python2.7で作ったほうがいいという判断。Python2.7は、文字列型が二つあって、パースするときとかに余計な不具合を起こすのだけれども、現実問題として、Ubuntuでよく使われるコマンドである「apt-get」なんていうのも、Python2.7で実装されている側面があり、それを考えるならば、無理矢理Python3でやるよりも、Python2.7で作ったほうがいいよね、と思ったのでそういう作りになっている。
ちなみに、Pythonでコンソール使用のアプリを作りたいときには、下のようなSetup.pyを作ってあげるといいらしい。
try: from setuptools import setup except ImportError: from distutils.core import setup setup(name='gimawari', version="0.0.1", packages=['gimawari'], entry_points="""# -*- Entry Points: -*- [console_scripts] gimawari = gimawari.console:begin """ )
ポイントは"[console_scripts]"を追加してあげることで、これによって、eggというPython専門のモジュール用圧縮ファイル(実はzipっぽい)を生成するときに、コンソールから呼び出すメインメソッドを指定してあげることができる。これによって、"gimawari"というコマンドを打つことによって、"gimawari.console:begin"というメソッドへ飛ぶことになる。
あと、常識的なアレとして、引数を習得するときには、下のようにやってあげる必要がある。
import sys
これによって「sys.argv」という引数を習得するための属性が使えるようになる。これに関しては、個別に変数に入れてあげるとか、そのあたりは好きなようにどうぞ。
あと細かいところでは、命令を実行したコマンドの状態を吐き出す為には、標準ライブラリであるところの「subprocess」を使用している。
subprocess – プロセスを生成して連携する - Python Module of the Week
callメソッドの場合は、配列の形で引数を渡してあげるのだが、shell=Trueとすると、shellに打ち込まれたワンラインをそのまま吐き出してくれる。それを、callメソッドからではなく、check_outputというメソッドから渡してあげれば、メッセージ出力がそのまま吐き出されたりする。
subprocess.check_out("git push" + origin_master[0] + " " + origin_maste[1],shell=True)
実は、Python2系列には、commandsというメソッドもあるらしく、最初はそれを使っていたんだけど、いろいろあって割愛。
システム的にはこんな感じです。コンソールでほげほげしたときには、Rubyで作るという人も多そうですけれども、Pythonでも意外に簡単にできるので、是非作ってみるといいんじゃないんでしょうか。
だいたい、自分は書きたいことが無くても、まず最初に道具を用意するタイプの人間で、もうそろそろ「あー、長めの長文とか気軽に公開できるサイトとか作ろうかなー」(特に書きたいこともないのに!!)と思ったのが、開発のキッカケでした。で、たぶんマルチバイト文化圏の人間にとって、Python3が「文字列は基本的にUnicode」という仕様はとても魅力的だなあと思っていて、これで何か作れないかなーと考えたところ、「そうだ、Blosxomクローンを作ろう!」と思ったのが、今回のきっかけです。
で、PythonによるBlosxom実装に関しては、既にPyblosxomというのがあって、改めて使うならば、そっちのほうが歴史もあり、今なお継続して製作されていることもあって、そっちのほうが良さげではあるんですけど、Python3という観点から見た場合は、なにかと大変なので、それだったら作ってしまおうと思った限り。
Blosxomが優れている点は、動的にPluginを組み替えることが出来る点にあると、個人的には思っているので、そうすると動的にプラグインを読み込むためのインターフェイスが必要になってくる。Pythonには、どうやら __import__ が存在しており、ここからPluginを読み込ませることが可能になっている。
def load_module(self,target_mod):
modules = __import__("lib." + target_mod)
for i,mod_item in enumerate(dir(modules)):
if mod_item == target_mod: return getattr(modules,dir(modules)[i])
例によれば、動的に読み込み、そこから何かしらの変数に結びつけるためには、getattrという関数を使えばいいらしい。しかし、複数のライブラリを読み込み、なおかつ配列の何番目に存在するのかが明確ではないため、enumerateで回したのちに、該当するPluginが読み込まれている場所がわかったら、その読み込まれた箇所をreturnするようにしている。
しかし、このような動的なプラグインで重要なのは、共通したインターフェイスになると思うので、今回はOutputというメソッドを共通のラインとして配置することにした。
例えば、記事をMarkdownで配置するためのプラグインは、以下のようなコードになっている。
#/usr/bin/env Python3
import markdown
def output(string):
string = markdown.markdown(string,['headerid(level=3)'])
return string;
if __name__ == "__main__":
print(output("ほげ\n----"))
共通インターフェイスはOutputに統一されているので、どんなライブラリが来ても、outputメソッドさえ呼べれば、それはダックタイピング?的なアレで、プラグインになる。
ちなみに、todo.pyというのも用意していて、これはScalaのPlayを扱っていた時に、TODOと書いておけば、とりあえずTODOが吐き出されて便利だったので作ったもの。PythonだったらPythonらしく"Pass"のほうが良かったかなーと思ったりもした。
あと、RSSを吐いたりするのも、Libraryにまかせておきたいと思ったので、Metaを実装。Metaを呼び込むことによって、Yaml Configで結びつけられたプラグインが呼び出される。
今後の課題としては、組み込み変数を無理やりパースしているので、こちらもコンフィグから呼び込めるようにするべきだなーとは思っている。!!PLUGIN::hoge!! みたいに呼び出すと、lib/hoge.py が呼び出されるみたいな方法にしてあげると、もうすこし拡張性が高まるなーと思う。(こういうミニマルシステムは、そこからいろいろと付け加えていける拡張性がポイントだと思うので)
あと、モジュール的に、Python3に対応していないものが多いので、一から書き直さなきゃいけない事例が多くなったりするのが大変。
もう一つとして、セキュリティー的にどうなの、という部分もまだ自信がないところなので、ちょこちょこと書き直していくかもしれない。
それは極めて単純な話であって、例えば、1年後や2年後において、ブログの記述システムというのは結構変わるから。例えば、このブログシステムは、現状としてHamlを使っているけれども、のちのちにHamlよりもベターなシステムが出てきた場合に、Templeteエンジンが気軽に変更できるのはいいことだと思う。記事を書くにしても、Markdownではないものを使いたいときに、Markdown以外のシステムが使えれば便利。
現状として、そのような記事のテンプレートエンジンが変更された場合に、ある記事ではMarkdownを使って、他の記事ではTexttileを使っている場合に、一貫して「Markdown」というわけにはいかないので、たぶん記事内に「これはMarkdownが使われていますよ~」的な、Meta記法を導入するかもしれない。
ここ最近、ジョークのプログラミング言語といえば、元がBrainF*ck系処理ばかりなのもどうなのかと思い始め、それだったらThe Shakespeare Programming Languageみたいな冗談言語でも作れないかなーと思って実装し始めた。ついでなので、JavaScriptのフレームワークテストの使い方も覚えておくといいよなーと思ったので、JasmineのStandaloneをダウンロードを使用して使っていた。
作り方としては、単純明快で、テストを書いた後に、そのテストが通る関数を実装してやるの繰り返し。テストの順番としては次のような感じ。
みたいな順番。割と順調にできたため、そんなに時間がかからなかった。
Jasmineを使った感想としては……うーん……普通に使いやすいテストフレームワークでした(小学生以下の感想)。でも、他の言語とは違って、JavaScriptの場合は、「普通に使いやすいテストフレームワーク」というのが作りにくいのかもなあと思ったりも。
で、ここからは、仕様メモ。
The Shakespare Programming LanguageはBrainF*ckに影響を受けているものの、それなりに違うところもある。違うところと言えば、数値と数値が掛け合わせたりできるところ。そして、それらは単語によって管理されているということ。この辺りは採用した。ただ、あまり複雑なことはできないので、最後の文字列で、その数字を変数(登場人物)に対してどのような操作を行うか、という形に実装してある。
次に、変数=登場人物があるというところと、宣言部があるところ。宣言部については、The Shakespare Programming Languageに則っている。元の言語によれば、登場人物はシェイクスピアの登場人物名しか採用できないらしいけれども、これはちょっとやりすぎだと思うので、登場人物の宣言と説明ができるようにした(そして、説明は全く無意味)。
さらに、「[」「]」みたいなものが存在しないため、少し難易度が下がってしまう。そこで、変数を式に埋め込める文法(『』)と、変数から変数に受け渡すことのできる文法を作る。これによって、ある文字を出力するさいに、効率よく変数に格納することによって、数字の操作がしやすくなる。
作ってみてわかったけど、これ単なるちょっと難しくなった電卓だね、ということで電卓としても使えるように、数字をそのまま出力する文法も設置した。
個人的な印象としては、既にBrainF*ck処理系というのは、その言語におけるちょっと難しいFizzBuzzみたいになっているのが正直な印象。ちょっと難しいFizzBuzzだけど、それなりにキャッチーな命令系統にしてあげると、割と面白がってくれるという側面はある。前に知人にたぶらかされてPythonでいきなり書いたりしたけど、そのときは2時間か3時間くらいかかった。自分でもそれくらいで作れるんだから、もっと上手い人なら一時間とか30分とか、そこらで作れるんだと思う(知人はそれくらいの速度で作っていた)。
たださすがにそれだけじゃ退屈な部分もあるので、自分なりに文法とか作ってみるといいんじゃないかと思う。そうすると、BrainF*ckがなぜそれなりに洗練されているのかっていうのもわかる(皆が好きな理由もわかる)。
そういうわけで、難解プログラミング言語は他にもいろいろあるわけなので、いろいろと研究されて、BrainF*ckだけではない、新しいSyntax(でも実際は変数の値をCharに直しているだけ)が出てくるといいなあと思ったりします。
ちょっと政治的な話題ではありますが、自民党が日本国憲法草案を公開していまして(それ自体の評価については、このブログでは書かないようにはしますが)、昔から、こういう党からの提出に限らず、草の根的に「自分たちが日本国憲法を作ったらどうなる?」という試みはあったりします。
なものなので、『日本国憲法』をMarkdownでマークアップしたものを、Githubに公開しましたので、これをgit cloneして弄り倒すといいかもしれません。ちょっとMarkdownも書きなれていないので、ところどころ不備があるのがちらほらあるので、そこの辺りを修正する予定です。
esehara/NihonkokuKenpo ? GitHub
ちなみに、Gitの使い方は、下が見やすいかな。
どんな使い方は皆様に任せます。gitの練習のお供にどうぞ。
ここ最近は、ずっとPHPばっかり弄っていたのですが、気分転換に、ちょっとGithubを見ていたら、Pythonプロダクトて魅力的なものを見つけ、ソースもそんなに無いから、適当に改善してCommitするかなーと思ったら、バグを発見したのでその辺のご報告。たぶん、日本語でPythonをいじっている人には自明の話。
恐らくPython3になって改善されていると推測されるであろう問題*1に、String型の問題があると思います。Python2.x系では、「文字列」を扱うTypeにはStringとUnicodeがあります。これはマルチバイト文化圏でPythonを使うたびに発狂する問題で(一度くらいはUnicodeEncodeErrorという文字列で発狂したことあるだろ!)、具体的にどんな悲劇をもたらしてきたかというのは、インタプリタの実行結果を見たほうが早い。
例えばlist型に変換する関数というのがあるけれども……。
>>> list("ほげほげ")
['\xe3', '\x81', '\xbb', '\xe3', '\x81', '\x92', '\xe3', '\x81', '\xbb', '\xe3', '\x81', '\x92']
という風に、1byteずつに分割されていく。
これでどんな悲劇が起きるかといえば、Stringをlistとして操作するとき。
>>> print "ほ"[0] >>> for i in "ほげほげ is dead.":print i, ... i s d e a d .
(念のため、Python2.x? ではprint文は空白を勝手にはさんでくれる)
で、このような破壊を防いでくれる役割というのが、Unicodeなんだけれども、String型前提でコーディングされているのだと、少々厳しい。
Issue #48: Writer converts everything to str ? kennethreitz/clint ? GitHub
こういう簡単なことは、1byte文字列の人にとっては、あまり思いつかないことでもあるし、Python2.xを使っている日本人がいたら、こういうバグを見かけてあげたら喜ばれるんじゃないかなーと思います。