25 posts tagged “python”
前作ったやつは、スタイル出力、HTML出力、スタイル埋め込みという手順を踏む必要があって面倒なので、pygmentsのAPIを使って一発で処理するようにしてみた。
前のは出力されたcssとHTMLを処理するのでstyle, lexerなんかは好きにすればよかったけど、
今回は1passなのでそこら辺の指定をできるようにしてみた。
from pygments import highlight
from pygments.lexers import get_lexer_by_name, get_lexer_for_filename
from pygments.formatters import get_formatter_by_name
from xml.etree.ElementTree import ElementTree, XML
import cssutils
from cssutils.css import CSSRule
from sys import stdout
from optparse import OptionParser
def write(filename, style_name='colorful', formatter_name='html', lexer_name=None, out=stdout):
fmtter = get_formatter_by_name(formatter_name, style=style_name)
if lexer_name:
lexer = get_lexer_by_name(lexer_name)
else:
lexer = get_lexer_for_filename(filename)
css = cssutils.parseString(fmtter.get_style_defs())
rules = dict([(x.selectorText, x.style.cssText.replace('\n', '')) for x in css.cssRules if x.type == CSSRule.STYLE_RULE])
tree = ElementTree(XML(highlight(open(filename).read(), lexer, fmtter)))
for elem in tree.getiterator():
c = elem.get('class', None)
if c:
style = rules.get('.'+c, None)
if style:
elem.attrib['style'] = style
tree.write(out)
if __name__ == '__main__':
optparser = OptionParser(usage=u'usage: %prog [options] file')
optparser.add_option('-S', '--style', dest='style', help='style', default='colorful')
optparser.add_option('-f', '--formatter', dest='formatter', help='formatter', default='html')
optparser.add_option('-L', '--lexer', dest='lexer', help='lexer')
options, args = optparser.parse_args()
if len(args) == 1:
write(args[0], options.style, options.formatter, options.lexer, stdout)
else:
print '** no input file specified **'
optparser.print_help()
ここでソース張っても色とかつけるのが大変なので、pygmentsのhtml出力とcssを結合するスクリプトを書いてみた。
embedstyle.py
import sys
from xml.etree.ElementTree import ElementTree
import cssutils
from cssutils.css import CSSRule
css = cssutils.parseFile(sys.argv[2])
rules = dict([(x.selectorText, x.style.cssText.replace('\n', '')) for x in css.cssRules if x.type == CSSRule.STYLE_RULE])
tree = ElementTree(file=open(sys.argv[1]))
for elem in tree.getiterator():
c = elem.get('class', None)
if c:
style = rules.get('.'+c, None)
if style:
elem.attrib['style'] = style
tree.write(sys.stdout)
使用例
スタイル生成→HTML出力→結合
pygmentize -f html -S colorful > test.csspygmentize -o embedstyle.html embedstyle.py
python embedstyle.py pkglist.html test.css > out.html
これで出力したのがこのページのコード
ディスクの空きがちょっと減ってきたので、サイズが大きいパッケージを知りたくて作ってみた。
python-aptとprettytable使ってます。
#!/usr/bin/python
import apt
from prettytable import PrettyTable
pt = PrettyTable(["package", "version", "size"])
pt.set_field_align("package", "l")
pt.set_field_align("version", "r")
pt.set_field_align("size", "r")
pt.set_padding_width(1)
SizeToStr = apt.SizeToStr
for v in sorted([x.installed for x in apt.cache.Cache() if x.isInstalled], key=lambda v: v.installed_size):
pt.add_row((v.package.name, v.version, SizeToStr(v.installed_size)))
print pt
出力
+----------------------------------------------------------+------------------------------------------+-------+
| package | version | size |
+----------------------------------------------------------+------------------------------------------+-------+
| adobe-certs | 1.0.8210 | 0 |
| adobeair1.0 | 1.5.1.8210 | 0 |
| tweetdeckfast.f9107117265db7542c1a806c8db837742ce14c21.1 | 0.22 | 0 |
| gnuplot | 4.2.5-2 | 20.5k |
| uim | 1:1.5.3-1 | 20.5k |
(省略)
| texlive-latex-extra-doc | 2007.dfsg.17-2ubuntu1 | 110M |
| openoffice.org-core | 1:3.1.0-3ubuntu2 | 113M |
| llvm-dev | 2.5+svn20090504-0ubuntu1 | 117M |
| ghc6 | 6.10.3-2ubuntu1 | 148M |
+----------------------------------------------------------+------------------------------------------+-------+
sys.stderrにメッセージを出力しているモジュールがあって、それを書き換えずに何とかしたいので考えてみた。
とりあえずうまくいったけど、いいのかわからない。
(追記
すっかり忘れてたけど、sys.stderrをほかの変数で参照してたらだめだった。当たり前なんだけど…
別モジュールで、from sys import stderrなら
モジュール名.stderrを書き換えればOK)
import sys
import logging
from StringIO import StringIO
class redir(object):
def __init__(self, f):
self.f = f
self.eol = True
def write(self, buf):
if buf == '\n':
if self.eol:
self.f('')
else:
self.eol = True
else:
self.eol = False
self.f(buf)
print >>sys.stderr, "hoge"
logging.basicConfig(filename='hoge.log', level=logging.DEBUG)
sys.stderr = redir(logging.error)
print >>sys.stderr, "hoge1"
print >>sys.stderr
print >>sys.stderr, "hoge2"
これで、最初の出力(hoge)はstderrに、以降の出力(hoge1,改行のみ,hoge2)はhoge.logに出力される。
調べ方メモ
6.6. The print statementを見て、出力先のオブジェクトにwriteが必要なことと、(必要なときは)最後に'\n'がくることはわかった。
writeに渡されるバッファに最後の改行が含まれているのか、改行だけ別に呼ばれるのか気になるので調べてみた。
適当な関数を作って、disで調べた。
>>> import dis
>>> def f():
... print >>None, None,
...
>>> def fln():
... print >>None, None
...
>>> def fnl():
... print >>None
...
>>> dis.dis(f)
2 0 LOAD_CONST 0 (None)
3 DUP_TOP
4 LOAD_CONST 0 (None)
7 ROT_TWO
8 PRINT_ITEM_TO
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
>>> dis.dis(fln)
2 0 LOAD_CONST 0 (None)
3 DUP_TOP
4 LOAD_CONST 0 (None)
7 ROT_TWO
8 PRINT_ITEM_TO
9 PRINT_NEWLINE_TO
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
>>> dis(fnl)
2 0 LOAD_CONST 0 (None)
3 PRINT_NEWLINE_TO
4 LOAD_CONST 0 (None)
7 RETURN_VALUE
>>>
たぶん、PRINT_ITEM_TOがデータの出力で、PRINT_NEWLINE_TOは改行であろうことはわかる。
改行付きのprintがPRINT_ITEM_TOと、PRINT_NEWLINE_TOと二つに分かれているので、2回に分けてwriteが呼ばれそうなこともわかる。
ともかく、PRINT_ITEM_TOとPRINT_NEWLINE_TOは間違いなくキーワードなので調べると、PRINT_ITEM_TOとPRINT_NEWLINE_TOは拡張版print statementで使われるのがわかる。それぞれ出力を行う命令なので、writeが二度呼ばれるものとして扱ってよさそう。
素直にそのままloggingすると、最後の改行までログに残ってしまうので邪魔。ただ、単純に'\n'だけ渡されたときに無視するようにすると、改行だけのやつ(fnlみたいなやつ)は捨てられてしまうので(ログに出すことしか考えていないので、許容できる場面もあると思うけど)簡単に対応しておく。
- 前回の出力が改行じゃないときは無視(PRINT_ITEM_TO->PRINT_NEWLINE_TOで呼ばれる場合)
- それ以外は空文字列を出力
Jythonの日本語処理がよくわからないについて。
UTF-8環境と仮定して、以下のようにすれば出来ます。
(コマンドプロンプトならCP932とかにすればいいんだけど、こまったことにjythonではcodecsに"cp932"がないのでencodeでSJISバイト列に出来ません。なのでJavaのAPIで出力するのしか出来なかった)
なぜかといえば、
>>> from java.lang import String, System
>>> from com.ibm.icu.text import Transliterator
>>> print Transliterator.getInstance("Fullwidth-Halfwidth").transliterate(String(u"テスト", "UTF-8")).encode("utf-8")
テスト
>>> System.out.println(Transliterator.getInstance("Fullwidth-Halfwidth").transliterate(String(u"テスト", "UTF-8")))
テスト
>>> sys.getdefaultencoding()
'ascii'
>>> System.getProperty("file.encoding")
'UTF-8'だからじゃないかな。
例えば、こんなのでも分かる。>>> "テスト".decode("utf-8")
u'\uff83\uff7d\uff84'
>>> print "テスト".decode("utf-8")
Traceback (most recent call last):
File "", line 1, in
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
試そうと思って入れたけど、IronPython StudioはIsolated Modeじゃないとだめで、F# InteractiveはIntegrated modeじゃないと駄目なのか。
両方(Isolated/Integrated mode)入れるはなんかもったいないな。IronPython Studioがintegrated modeで動くと良いんだけど…
tracもpythonで書いてあるんだから、psyco使ったら速くなるかな?とふと思ったので試してみた。
私の所では、mod_pythonを使っているので、trac/web/modpython_frontend.pyに以下を追加して試した。
abで負荷かけてみたら、平均だと20msecぐらい速くなった。
import psyco
psyco.profile()
min 154→130
mean 161→137
median 159→136
max 810→531
1000リクエストにかかった時間が、160sec→138secで22秒差だから大体あってるかな。
pythonスレをみてて気になったので調べた結果。
入力フォームで全角のみを許可したいという話なので、全角(Full-width)かどうかの判定をunicodedataでやれば楽ができそうと思った(853)。
ただ、入力がUnicodeの場合は分解されている可能性があるので、正規合成しなくても大丈夫か気になった。
ということで、特に問題ないのかな?
Python 2.5.1 (r251:54863, Nov 19 2007, 17:32:14)[GCC 4.2.2 (Gentoo 4.2.2 p1.0)] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import unicodedata>>> for uc in unicodedata.normalize('NFD', u'ぱいそん'):... uc, unicodedata.east_asian_width(uc)...(u'\u306f', 'W')(u'\u309a', 'W')(u'\u3044', 'W')(u'\u305d', 'W')(u'\u3093', 'W')>>>
まあ、合成した方がわかりやすい気がするけど。
unicodedataとicuは出来ることがかぶってる~って書こうと思ったら、UAX #11: East Asian Widthを見つけた。PyICUは開発中で、現状ではuchar/UCharacterに該当するものが無いから、この件に関してはunicodedataを使うしかないけど。
正規化は便利だし、なんかおもしろい。
入力文字種を制限するんじゃなくて、入力を正規化して、確認画面で正しいかどうかをユーザに判断してもらうのもありなんじゃないかな。
まあその場合、NFC+Full-Widthに統一かな。NFKCだとHalf→Fullまで出来るけど、複数文字を1文字にしたのがばらされてしまう。それが問題にならなければ楽。
>>> print unicodedata.normalize(u'NFKC', u'㍊㌶㍻㎡パイソンぱいそん')ミリバールヘクタール平成m2パイソンぱいそん
python+cmecabを使って、chasen server(chasend?)もどきを書きました。
LuceneでJapaneseAnalyzer+ChasenTokenizerで使う事しか考えてないけど、とりあえず使える。
いまいち仕様がわからないので、ChasenTokenizerのソースとmecab -Ochasenの出力を見て書いてみた。