attribute: Phillie Casablanca

Category: python (12)

pickleの弱点



PythonでDBのこと(とくにslqlite)知った前、データの保存するなら、自分が定義するCSVファイルとかより、pickleを使っていました。 pickle/cPickleはPythonのオブジェクットをそのままでファイルで保存することが出来ます。

[code]
import cPickle

object_dict = {'1':'一つ','2':'二つ'}

file_obj = open('test.pkl','w')

cPickle.dump(object_dict,file_obj)
file_obj.close()

#データを読み込む場合
file_obj = open('test.pkl','r')
object_data2 = cPickle.load(file_obj)
[/code]

楽でしょう。

しかし、この間、pickleでデータを保存してみたら、データが完全に保存されなかったです。
わかるまで一日ぐらいかかったが、自分のオブジェクト(クラス)を定義した場合、メモリ上で存在しているオブジェクットしか参照しないようです。

[code]
>>> import pickle
>>>
>>> class myclass:
... myattr = None

>>> x = myclass()
>>> x.myattr = 1

>>> # これでpickleとして本尊するのはまだいいのですが。。。
>>> file_obj = open('file.pkl','w')
>>> pickle.dump(x,file_obj)

>>> recovered_x = pickle.load(file_obj)
>>> recovered_x.myattr
1
[/code]

ここで問題なくロードができましたが、セッションを閉じたら、問題になってしまう。

[code]
# 一回 exit()して、再開やってみる
>>> exit()
PS D:\> python
Python 2.5.2 (r252:60911, Feb 21 2008, 13:11:45) [MSC v.1310 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import pickle
>>>
>>> file_obj = open('file.pkl','r')
>>> recovered_x = pickle.load(file_obj)
Traceback (most recent call last):
File "", line 1, in
File "C:\python25\Lib\pickle.py", line 1370, in load
return Unpickler(file).load()
File "C:\python25\Lib\pickle.py", line 858, in load
dispatch[key](self)
File "C:\python25\Lib\pickle.py", line 1069, in load_inst
klass = self.find_class(module, name)
File "C:\python25\Lib\pickle.py", line 1126, in find_class
klass = getattr(mod, name)
AttributeError: 'module' object has no attribute 'myclass'
>>>
[/code]

これでできないようにみえますが、実は出来ます。
もっとのmyclassの定義が見つかっていないからだけです。
class_mod.pyというファイルにクラスの定義を保存して、同じことやってみたら、行きます。

しかし、問題はこれからです。
class_mod.pyに新しいクラスを追加します:

[class_mod.py code]
class myclass:
myattr = None

class newclass:
sub_class = myclass()
[/class_mod.py code]

これでデータを保存してみよう。

[code]
>>> import class_mod
>>> import pickle
>>>
>>> x = class_mod.newclass()
>>>
>>> x.sub_class.myattr
>>> x.sub_class.myattr = 1
>>> x.sub_class.myattr
1
>>>
>>> file_obj = open('file.pkl','w')
>>> pickle.dump(x,file_obj)
>>>
>>> exit()
PS D:\> python
Python 2.5.2 (r252:60911, Feb 21 2008, 13:11:45) [MSC v.1310 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import pickle
>>>
>>> file_obj = open('file.pkl','r')
>>> recovered_x = pickle.load(file_obj)
>>>
>>> recovered_x.sub_class.myattr
>>>
>>> recovered_x.sub_class.myattr is None
True
>>>
[/code]

単純なクラスなら保存ができるようですが、定義しているクラスが別クラスに参照しているなら、pickleは簡単には使えないです。
今回は必要のデータをDictionaryに変換する関数を作って、そのDictionaryをPickleしています。
それでそのデータをまたクラスに記入する関数を作りました。

やっと問題がわかって、解決ができたが、これくらいかかるようだったら、最初から、Dictionaryだけを使うとかsqliteを使うとかはしました。


monkut // May 9, 2008 // 7:59 a.m.

サーバへ自動転送とsubprocessのタイムアウト



この間いくつかの問題を出会った。

一つは、リモートサーバでファイルの編集したくて、今使っているEclipseでリモートアクセスPluginとかを探していたが、見つかったPluginのインストールがうまく行かなくて、あきらめて、自分でファイルを転送するスクリプトを作ってみた。
(今までは、サーバ側、VIで編集していたが、得意じゃないので、開発が遅い)

Eclipseで保存したら、ファイルが自動にサーバへ転送されるのが要望。
複数ファイルを編集している可能性があるので、DIR内全てのファイルをターゲットしたい。

まず、ファイルが変更されたかどうかの確認方法は、os.path.getmtime()。

つまり、mtime(最後に変更(Modify)された時間)を記録して、変わったら、そのファイルを転送する。

それでファイルのパスとそのmtimeの記録をDICTにまとめて返す関数を作った:


def _get_local_files(local_dir):

return [
{
          'path':os.path.join(local_dir,file),
'mtime':os.path.getmtime(os.path.join(local_dir,file))
         }
for file in os.listdir(local_dir)
if os.path.isfile(os.path.join(local_dir,file))
]

ここでリストComprehensionを使ってみた。リストComprehensionは他人を読むとわかりずらくて、あまり、使うのは好きじゃないけど、たまにはたのしい。

それで変更を監視して、転送を実行する部分:


last_files_list = _get_local_files(local_dir)

while True:
try:
time.sleep(SLEEP_SECONDS)

latest_files_list = _get_local_files(local_dir)

files_to_update = []

for idx in xrange(len(latest_files_list)):
if idx < len(last_files_list):
if latest_files_list[idx]['mtime'] > last_files_list[idx]['mtime']:
files_to_update.append(latest_files_list[idx])

else:
files_to_update.append(latest_files_list[idx])

if files_to_update:

print '変更あり、転送実行~'

is_success = upload_all(server,username,password,local_dir, remote_dir, files_to_update, encrypt)

if not is_success:
break

else:
print '.',

last_files_list = latest_files_list[:] # copy the list to hold
except KeyboardInterrupt:
print
print 'Exiting.'
break

これで失敗しない限り、監視がCtrl-Cを押すまで続く。

じゃ、upload_all()の中身、まず、ftplibを使ったんですが、やっぱり、sftpを使いたい。
軽く調べてみたら、Pythonのいいsftpモジュールが見つからなくて、結局、Putty (www.putty.org) についているpsftpをラップして、ftplib.FTP()と同じようなインタフェースを作った。

要は、ラッパクラスを作って、このメッソドを加えた:

class SftpHandler:

def connect()
def login()
def cwd()
def storbinary()
def quit()

subprocess.Popenでpsftpを起動させて、PIPEでコマンドのやり取りをやればと思った。
クラス Popen( args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)
http://www.python.jp/doc/release/lib/node229.html

そこで、問題発生。

この感じで読んでいた:


proc_args = ('C:\\Putty\\psftp.exe', '<サーバ>')
self.proc = subprocess.Popen(proc_args,
      bufsize=0,
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)

ログインのところでユーザ名が完全に送られていないみたい。

self.proc.stdin.write('myusername\n')
self.proc.stdin.flush()

まず、bufsizeを1(行ごとにバッファされること)にしてみた。
多少よくなったが、最後の文字がとられていた。よく考えてみたら、WINDOWSの改行コードが'\r\n'、それに変更したら、うまく行った。

つまり:

proc_args = ('C:\\Putty\\psftp.exe', '<サーバ>')
self.proc = subprocess.Popen(proc_args,
      bufsize=1,
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)

self.proc.stdin.write('username\r\n')
self.proc.stdin.flush()

ここまでは、psftpからの出力を読まなくても、済んだが、パスワードのプロンプトやコマンドプロンプトがちゃっとでているかどうかは知りたい。そこでプロセスのstdoutを読めば、できるが、proc.stdout.read()はなんかくるまでずっと待つ。

stdout.read()のタイムアウトがほしいよね。

もし、タイムアウトがあったら、そこで、プロンプトがないと判断できる。

まず、ほしいプロンプトがあるかどうかの判断はこんな感じでやった:


def _wait_for_text(self, text = 'login as'):
resp = ''
while text not in resp:
resp += self.proc.stdout.read(1)

self.proc.stdout.flush()
return True

じゃ、このread()が待ち状態にとまったら、タイムアウトして、Falseを返したい。
timeoutをさがたら、いくつかの答えはでるけど、これが一番きれいで簡単かなと思った:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473878

これをread()のタイムアウトとして使いたいので、下記のように編集した:


def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None):
'''This function will spwan a thread and
return the given default value if the timeout_duration is exceeded or the function all raises an exception.
'''
import threading
class InterruptableThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.result = default
def run(self):
try:
self.result = func(*args, **kwargs)
except:
self.result = default
it = InterruptableThread()
it.start()
it.join(timeout_duration)
if it.isAlive():
return it.result
else:
return it.result

このtimeoutを使って、_wait_for_text()をラップするような感じで、呼ぶ。
そうすると、時間が切れたら、Falseを返して、切る前ならTrueを返す。

ラップするとこうな呼びかた:


prompt_found = timeout(self._wait_for_text,
args=(password_prompt,),
timeout_duration=PROCESS_TIMEOUT_SEC,
default=False )

FalseならプロセスをKILLしたいなら、ウインドウズでこの方法がある:
(win32apiを使う方法)


import win32api
PROCESS_TERMINATE = 1
handle = win32api.OpenProcess(PROCESS_TERMINATE, False, self.proc.pid)
win32api.TerminateProcess(handle, -1)
win32api.CloseHandle(handle)

subprocessを使って、初めてプロセスのPIPEでやりとりが出来た。
かなり勉強になった!

monkut // June 30, 2008 // 8:58 a.m.

zlibとsqlite3の組み合わせ



Python 2.5で追加されたsqlite3モジュールで簡単にデータベースを使うことができる。

sqliteでの一つの特徴は、サーバプロセスはいらなくて、データベース自体が一つのファイルになる。
これはすごく便利!

しかし、そのファイルが大きくなるとテーブルや値のQuery時間(検索時間)が遅くなる。
そこで文字列を入れているなら、zlibを使って、圧縮して入れれば、ファイルサイズが小さくなって、検索時間が上がるではないかと思っていた。
(圧縮・解凍プロセス時間が発生するが、自分のデータによってある程度改善されるかも)

とりあえず、その方法を紹介する。

まず、DBの構築、さっき言ったようにこれは簡単。


import sqlite3

# ここでメモリ上のDBを作成。パスとファイル名を指定してあげれば、ファイルDBができる
con = sqlite3.connect(':memory:')
cur = con.cursor()

table_query = u'''CREATE TABLE testtable (binval)'''
cur.execute(table_query)
con.commit()

じゃ、データベースが出来て、これからデータの用意:

# Unicode
mojiretsu = u'''日本語をいれてみよう。  問題にならないといいな~'''

それでデータの圧縮:

import zlib
compstr = zlib.compress(mojiretsu)

しかし、これでだめだった

Traceback (most recent call last):
File "stdin", line 1, in module
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

失敗しちゃったよ。 なぜかここのPCではasciiでエンコードしようとしているようだ。
じゃ、zlib.compress()に渡す前、utf8に変換しよう。

mojiretsu_utf8 = mojiretsu.encode('utf8')
compstr = zlib.compress(mojiretsu_utf8)

文字列の圧縮が出来た。
データベースに入れるところだが、ここでなかなか分からなかったことは、どうやって、バイナリデータのままでデータベースへの挿入ができる?いろいろ探してみたところ、sqlite3.Binary()を使うと動いたようだ。

query = u'''insert into testtable VALUES(?)'''
b = sqlite3.Binary(compstr)

cur.execute(query,(b,))
[sqlite3.Cursor object at 0x00CB035]

con.commit()

解凍が可能かどうかを確認しよう!

result_obj = cur.execute('''SELECT * FROM bintest''')
rows = result_obj.fetchall()
rows
[(read-write buffer ptr 0x00BE8F20, size 80 at 0x00BE8F00,)]
first_row = rows[0][0]

kaitou_mojiretsu = zlib.decompress(first_row)
print kaitou_mojiretsu
日本語をいれてみよう。  問題にならないといいな~

参照モジュール:
zlib (日本語)
http://www.pinkdragon.net/doc_lib/contents/ja/python_man/lib/module-zlib.html

sqlite3 (英語)
http://docs.python.org/lib/module-sqlite3.html

monkut // July 3, 2008 // 2:03 a.m.

ウインドウズで解像度を取得方法



たまにはこの情報は必要です。


from win32api import GetSystemMetrics
print "幅 =", GetSystemMetrics (0)
print "高さ =",GetSystemMetrics (1)



monkut // July 29, 2008 // 9:38 p.m.

ウインドウズでユーザ名を取得する方法



この関数は見つかりにくかったですが、ユーザ名は簡単に取れます。


import win32api

username = win32api.GetUserName()



monkut // Sept. 10, 2008 // 2:53 a.m.

minidomでattributeを取得方法



XMLをパースするには、なかなかいい方法は見つかっていないです。

今は、minidomを使っています。
今日、使ってみたら、ノードからのAttributeを取得方法が分からなくて困っていました。

いろいろ検索したら、この答えがでてきました:


from xml.dom import minidom

rawdoc = """\
<testresults>
<test total="2" fail="1">
<testcase title="testcase1" fail="True">
<reason>This test case failed, given file not found!</reason>
</testcase>
<testcase title="testcase2" fail="False">
<reason/>
</testcase>
</testresults>
"""

xmldoc = minidom.parseString(rawdoc)

for node in xmldoc.documentElement.childNodes:
if node.attributes:
for i in range(node.attributes.length):
a = node.attributes.item(i)
print "%s = %s" % (a.name, a.value)



monkut // Sept. 16, 2008 // 1:11 a.m.

株解析スクリプト



何年前か、初めて、株の流れを簡単に解析スクリプトを作ってみました。

買うトリガーと売るトリガーを出してくれるようなスクリプトでした。
実際に実行しなかったんですが、そろそろ、再開しようかなと考えています。

何年前のスクリプトなので、その時により詳しくなったかなと思って、全て0から作り直してみます。

まず、株情報とるには、このスクリプトがありました:
http://www.goldb.org/ystockquote.html

次、そのデータをHDDにDBを作ろうと考えています。
(いつも、ネット繋がった状態には開発ができないので、試せるデータがほしいです)

また、できたら報告します。

monkut // Sept. 19, 2008 // 9:41 a.m.

G1 (Google Android phone)とpython?



やっと、初のAndroid携帯が発表されました!
http://www.gizmodo.jp/2008/09/androidtmobile_g1ui.html

Androidの何がいいかというと、iphoneと違って自分のアプリは自由に開発できることです。

どうすれば開発できるでしょうか?

まず、SDKをダウンロード:
(Eclipseのプラグインがあるようです)
http://code.google.com/intl/ja/android/intro/index.html

Androidのハローワールド:
http://code.google.com/intl/ja/android/intro/hello-android.html

これぐらいのJAVAは出来そうですが、やっぱりpythonで開発したいですよね。

それは、まだ、できるかどうか明確されていないです。

調べてみたら、やろうとしている人がいるようですが、まだ、成功例が見つからないです。
http://groups.google.com/group/android-developers/msg/2f57efd7330ee641

Javaだから、Jythonでできるんじゃないと質問があがってくるんですが、AndroidはJavaのフォークと言われているので、JythonはそのVMの上に動くかどうかはまだ分からないです。

Googleだから、Pythonはよく使っているので、実現的なら、PythonでAndroid開発できる日がでてくるでしょう。
(ハローワールドにコマンドラインでプロジェクト作成にはPythonツールをつかっています。"activitycreator.py")


monkut // Sept. 24, 2008 // 1:54 a.m.

python 2.6とeggsとsetuptools



python 2.6がリリースされました!

multiprocessing (http://docs.python.org/library/multiprocessing.html#module-multiprocessing) のモジュールが追加されました!これでGILの問題が解消されるじゃないですかね?

あと、subprocessのモジュールにterminate(), kill(), と send_signal()がPopenのオブジェクットに追加されました!

http://docs.python.org/whatsnew/2.6.html

Download:
http://www.python.org/download/releases/2.6/

使ってみたら特に問題がぶつかっていないです。

pywin32の3rdPartyモジュールのインストーラはすでに2.6を対応しています:
http://downloads.sourceforge.net/pywin32/pywin32-212.win32-py2.6.exe?modtime=1217537338&big_mirror=0&filesize=6134129

しかし、最近使っているSphinx (http://sphinx.pocoo.org/)を使いたくて、easy_installをインストールするのが一番やりやすい方法のようです。じゃ、EasyInstall(http://peak.telecommunity.com/DevCenter/EasyInstall#installing-easy-install)はsetuptoolsに入っていて、まだ、2.6用のウインドウズのインストーラがないです。

じゃ、どうすればいいでしょうか?

ここで分かりました。
http://thinkhole.org/wp/2007/02/01/howto-install-setuptools-in-windows/

まず、setuptoolsからez_setup.pyをダウンロードして、実行します。
http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06

それでPathにpython26/Scriptsが入っているかどうかの確認で終わり。

じゃ、Sphinxをインストールするには下記のとおりです:
easy_install Sphinx

できました!


monkut // Oct. 13, 2008 // 11:25 p.m.

psftplib google code project started!



I setup my first google code project today.
http://code.google.com/p/psftplib/

Posting a sftp wrapper for psftp I made for windows.

There may be other sftp options available now, but at the time it was easiest to just wrapper the psftp tool provided in the putty package. (I was already using putty for ssh in windows).

It's incomplete, I only added the functionality I needed, and just started to fill out the other ftplib functions that may be useful. Now that it's available on google code hopefully someone else can get some use out of it.

monkut // Feb. 24, 2009 // 12:33 p.m.

リストのループするときにIDXを生成してくれるenumerate()があった



リスト([])があって、それをループすると同時にインデックスが欲しいです。

よくあるパーターンですね。

今まではこのパーターンでループしていました:


x = ['a', 'b', 'c']
idx = 0
for i in x:
print idx, i
idx += 1

この間、enumerate()を見つけました。
これを使うとこうなります:


for idx, i in enumerate(x):
print idx, i


ちょっと、楽になりました。

monkut // March 31, 2009 // 2:36 a.m.

Creating PDFs containing Japanese from Sphinx's *.tex output



I've been struggling to get sphinx (http://sphinx.pocoo.org/index.html) output working properly with Japanese text. (I assume this will also work with Chinese and Korean as well, but I haven't tested, and the encoding portion may not work or need to be changed)

I've finally got a solution that works for me.

Previously I had been using MiKTeX to convert sphinx's *.tex output to pdf, but hit a wall when trying to convert Japanese characters.

Latex seems to be a mess when dealing with non-ascii characters.

Using w32tex (http://www.fsci.fuk.kindai.ac.jp/kakuto/win32-ptex/web2c75.html) I was able to successfully convert the output *.tex file Sphinx creates to pdf.

The method I used is described below.

Installation Steps
====================

1. Create C:\w32tex directory

2. Create C:\w32tex\archivedpackages directory

3. Download "texinst757.zip" from http://www.fsci.fuk.kindai.ac.jp/kakuto/win32-ptex/web2c75.html

4. Unzip "texinst757.zip" into C:\w32tex

5. Download ALL files from the "最小インストール"(Minimal Install) and "標準インストール" (Standard install) lists and place them in C:\w32tex\archivedpackages

6. In addition download the following packages from the "フルインストール" (Full Install) list


ums.tar.gz
omegaj-w32.tar.gz
ttf2pt1-w32.tar.bz2
utf.tar.gz -- not sure if it's needed
uptex-w32.tar.bz2 -- not sure if it's needed

7. Download and install ghostscript ftp://akagi.ms.u-tokyo.ac.jp/pub/TeX/win32-gs/gs863w32full-gpl.zip
A. Check the "Use Windows TrueType fonts for Chinese, Japanese and Korean" option and Install into C:\w32tex\gs

8. Download and install IPA fonts from http://lx1.avasys.jp/OpenPrintingProject/openprinting-jp-0.1.3.tar.gz
A. Drag and drop the *.ttf files found in the archive to the Windows/Fonts directory.
- this will install the fonts on the pc

9. Run the following command:

	
texinst757.exe C:\w32tex\archivedpackages

NOTE: This will take some time

10. Add the following to your system path:

	
C:\w32tex\bin
C:\w32tex\gs\gs8.63\bin
C:\w32tex\gs\gs8.63\lib


11. Download ftp://cam.ctan.org/tex-archive/macros/latex/contrib/titlesec.zip, unzip and copy the titlesec folder to C:\w32tex\share\texmf\latex\

12. Run the following command (You may need to restart your console in order to take the new path settings into effect)
- This command will search and update fonts/styles (*i think*) that were added in step 11.


texhash

Installation is now Done!

Converting Sphinx *.tex output to PDF
========================================

Now that you have w32tex installed, you still need to adjust some of your process in order to create a pdf properly.
Note: I'm using sphinx 0.6.1

1. In your conf.py add/uncomment the latex_elements and update to look like this:


latex_elements = {
'preamble': '\usepackage{ums}\input jpdftextounicode\pdfgentounicode=1',
'inputenc': '',
'fontenc': '',
'fontpkg': '',
}

2. Build your *.tex file using sphinx.
- My source files are utf8, and I believe sphinx outputs the *.tex to utf8.

3. Convert the output text to cp932 encoding
- Ideally, topdftex.exe/pdflatex.exe support utf8, but I haven't figured this out, so this is currently a workaround step


import codecs
utf8_f = codecs.open('sphinx_output.tex', 'rb', 'utf8')
cp932_f = codecs.open('sphinx_output.cp932.tex', 'wb', 'cp932')
cp932_f.write(utf8_f.read())
utf8_f.close()
cp932_f.close()

4. Run topdftex.exe on the converted output, renaming to the orignal *.tex name.


topdftex.exe sphinx_output.cp932.tex sphinx_output.tex

5. Run pdflatex.exe on the final output, sphinx_output.tex.

I hope this helps.

monkut // April 22, 2009 // 3:46 a.m.