PythonでExcelを読み書きするライブラリの1つとして、openpyxlがあります。
公式サイトには列のグルーピングについては記載されていましたが、行のグルーピングについては記載されていませんでした。
ソースを参照しながら工夫したらどうにかできたので公開します。
今後公式から行のグルーピングについてアナウンスされるかもしれませんので、ご留意ください。
以下、ソースです。
2018年3月19日月曜日
2014年9月29日月曜日
行指向ファイル処理フレームワーク
業務でファイルを一行ずつ読み込んで処理するプログラムをコーディングしているのですが、提供された設計書のフローチャートに従って実装を進めていたら、脳みそがぐちゃぐちゃにかき回されるようないらだちを覚えました。
"ファイルを一行ずつ読み込んで処理する"という要件は、あちこちで需要がありそうなので、モデルを考えてみました。 Process、FileFinder、FileHandler、LineHandlerの4つのクラスで構成します。
"ファイルを一行ずつ読み込んで処理する"という要件は、あちこちで需要がありそうなので、モデルを考えてみました。 Process、FileFinder、FileHandler、LineHandlerの4つのクラスで構成します。
2014年7月14日月曜日
ちょっとしたツールを作るのに便利なPythonのcmdモジュールを使うための3ステップ
シンプルなコマンドラインツールが必要なとき、Pythonのcmdモジュールが有力な選択肢になります。ツールを実装するために必要な3ステップを紹介します。
以下Tips。必須ではありませんが、知っておくと便利です。
- cmd.Cmdクラスを継承したクラスを作る
- do_XXXメソッドを定義する
- cmdloopメソッドを呼ぶ
cmd.Cmdクラスを継承したクラスを作る
特に言うべきことはありません。普通に継承するだけです。
class HelloCmd(cmd.Cmd): ...
do_XXXメソッドを定義する
メソッド名の"XXX"がコマンドになります。
パラメータを受け取るためのargパラメータがポイントです。
def do_hello(self, arg): print 'hello, ' + str(arg)
cmdloopメソッドを呼ぶ
クラスをインスタンス化し、cmdloopを呼ぶとREPLがスタートします。
終了するにはCtrl-Cで落とします。
>>> HelloCmd().cmdloop() (Cmd) hello Taro Hello, Taro (Cmd) Traceback (most recent call last): File "", line 1, in File "/usr/lib/python2.7/cmd.py", line 130, in cmdloop line = raw_input(self.prompt) KeyboardInterrupt
以下Tips。必須ではありませんが、知っておくと便利です。
2014年5月5日月曜日
unittest.mockの簡単な紹介
単体テストに欠かせないツールのひとつに、モックがあります。
Pythonにも3.3からモックモジュールが追加されました。
unittest.mockといいます。今回はこのモジュールの簡単な使い方を紹介します。
Pythonにも3.3からモックモジュールが追加されました。
unittest.mockといいます。今回はこのモジュールの簡単な使い方を紹介します。
モックが必要になる場面として、システム日付の取得があります。
例えば、当日が閏日かどうか判定する関数を考えてみましょう。
なにも考えずに関数を実装すると、次のようになると思います。
def is_leap(): d = datetime.date.today() if 2 == d.month and 29 == d.day: print(str(d) + ' is LEAP') return True else: print(str(d) + ' is NOT LEAP') return False
このままではOSの時計を設定しないと試験ができないので、
日付をパラメータとして渡すようにします。
日付をパラメータとして渡すようにします。
def is_leap(d): if 2 == d.month and 29 == d.day: print(str(d) + ' is LEAP') return True else: print(str(d) + ' is NOT LEAP') return False
日付を注入できるようにしたことで、テストしやすくなりました。
例えば、このようにします。
例えば、このようにします。
assert True == is_leap(datetime.date(2012, 2, 29)) assert False == is_leap(datetime.date(2012, 3, 1))
unittest.mockを使うと、次のような感じになります。
d = unittest.mock.MagicMock() d.month = 2 d.day = 29 assert True == is_leap(d)
モックを使わないほうが単純です。こういう場合はdateオブジェクトを使ったほうがいいでしょう。
さて、現実には全てのコードを自由に変更できるとは限りません。
たとえそんな場合でも、unittest.mock.patchを呼ぶことで、対応できる場合があります。
is_leap関数を修正せずに次のようにします。
さて、現実には全てのコードを自由に変更できるとは限りません。
たとえそんな場合でも、unittest.mock.patchを呼ぶことで、対応できる場合があります。
is_leap関数を修正せずに次のようにします。
with unittest.mock.patch('datetime.date') as dt: dt.today().month = 2 dt.today().day = 29 assert True == is_leap()
datetime.date.today()メソッドが返すオブジェクトをモックオブジェクトと置き換え、
属性を設定することでテストをパスしています。
確かにこの方法でも試験はできますが、日付をパラメータ化するほうがベターだと思います。
他にも、unittest.mockにはメソッドの呼び出しを記録する機能もあります。
詳しいことはオフィシャルのドキュメントを参照してください。
属性を設定することでテストをパスしています。
確かにこの方法でも試験はできますが、日付をパラメータ化するほうがベターだと思います。
他にも、unittest.mockにはメソッドの呼び出しを記録する機能もあります。
詳しいことはオフィシャルのドキュメントを参照してください。
参考:
2014年3月24日月曜日
PythonでEvernoteへメールを送る - 添付ファイル追加
「PythonでEvernoteへメールを送る」で載せたプログラムへ、ファイル添付機能を追加しました。
合わせてクラス設計を見直しました。
複数ファイルを添付するとうまくいかないところが問題です。
また時間のあるときに対応したいと思います。
合わせてクラス設計を見直しました。
# -*- coding: utf-8 -*- # ---- CONFIGURATIONS ---- EVERNOTE_MAIL_ADDRESS = 'evernote_mail_address' FROM_ADDRESS = 'from_address' SMTP_HOST = 'smtp_host' SMTP_PORT = 25 # ---- CONFIGURATIONS ---- import datetime import mimetypes import optparse import os.path import smtplib import sys from email import encoders from email.header import Header from email.mime.audio import MIMEAudio from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText class EverMailClient(object): def __init__(self, smtp): assert isinstance(smtp, smtplib.SMTP) self._smtp = smtp def send(self, mail): assert isinstance(mail, EverMail) msg = mail.as_MIME() mail_from = msg['From'] mail_to = msg['To'] self._smtp.sendmail(mail_from, mail_to, msg.as_string()) class EverMail(object): def __init__(self, title, from_address, to_address, body, attached_files): assert isinstance(title, EverMailTitle) assert isinstance(body, EverMailBody) self._title = title self._from_address = from_address self._to_address = to_address self._body = body self._attached_files = attached_files def as_MIME(self): msg = MIMEMultipart() msg['Subject'] = Header(str(self._title), 'utf-8') msg['From'] = self._from_address msg['To'] = self._to_address msg.attach(self._body.as_MIME()) for attached_file in self._attached_files: msg.attach(attached_file.as_MIME()) return msg class EverMailTitle(object): def __init__(self, title, notebook, tags): self._title = title if title else str(datetime.datetime.now()) self._notebook = notebook if notebook else '' self._tags = tags if tags else [] def __str__(self): result = self._title if 0 < len(self._notebook): result += ' @' + self._notebook for tag in self._tags: result += ' #' + tag return result class EverMailBody(object): def __init__(self, stdin): assert hasattr(stdin, 'read') self._stdin = stdin def as_MIME(self): body = sys.stdin.read() msg = MIMEText(bytes(body)) msg.set_charset('utf-8') return msg class AttachedFile(object): def __init__(self, path): self._path = path def as_MIME(self): ctype, encoding = mimetypes.guess_type(path) if ctype is None or encoding is not None: ctype = 'application/octet-stream' maintype, subtype = ctype.split('/', 1) msg = None with open(self._path, 'rb') as f: if maintype == 'text': msg = MIMEText(f.read(), _subtype=subtype) elif maintype == 'image': msg = MIMEImage(f.read(), _subtype=subtype) elif maintype == 'audio': msg = MIMEAudio(f.read(), _subtype=subtype) else: msg = MIMEBase(maintype, subtype) msg.set_payload(f.read()) encoders.encode_base64(msg) file_name = os.path.basename(self._path) msg.add_header('Content-Disposition', 'attachment', filename=file_name) return msg class EverMailOptionParser(optparse.OptionParser): def __init__(self): optparse.OptionParser.__init__(self) self.add_option('--notebook', '-n', action='store') self.add_option('--tag', '-t', action='append') def create_smtp_client(): smtp = smtplib.SMTP(SMTP_HOST, SMTP_PORT) smtp.set_debuglevel(1) return smtp if __name__ == '__main__': option_parser = EverMailOptionParser() (options, args) = option_parser.parse_args() note_title = args[0] if args and 0 < len(args) else '' title = EverMailTitle(note_title, options.notebook, options.tag) body = EverMailBody(sys.stdin) attached_files = [] if 2 <= len(args): attached_files = [AttachedFile(path) for path in args[1]] mail = EverMail(title, FROM_ADDRESS, EVERNOTE_MAIL_ADDRESS, body, attached_files) smtp = None try: smtp = create_smtp_client() client = EverMailClient(smtp) client.send(mail) finally: if smtp: smtp.close()
複数ファイルを添付するとうまくいかないところが問題です。
また時間のあるときに対応したいと思います。
2014年3月17日月曜日
PythonでEvernoteへメールを送る
Evernoteへメールを送ることでノートを追加できるのはご存知かと思います。
最近、古いネットブックをUSBブートのLinuxで使用しています。ネットブック上で作成したファイルをEvernoteへアップロードしたいと思うことがあるのですが、ブラウザから操作するのは厳しいと感じています。
Pythonからメールでファイルを送るようにすればいいのではないかと思い、標準入力から読み込んだ内容をEvernoteへ送るプログラムを書きました。
プログラム
使用例
工夫したところは、optparseモジュールを使ってオプションの解析を実装したところです。
暇があれば、画像の添付なども対応したいと思います。
参考
15.5. optparse — コマンドラインオプション解析器
Eメールを送信するだけでEvernoteに簡単送信!
最近、古いネットブックをUSBブートのLinuxで使用しています。ネットブック上で作成したファイルをEvernoteへアップロードしたいと思うことがあるのですが、ブラウザから操作するのは厳しいと感じています。
Pythonからメールでファイルを送るようにすればいいのではないかと思い、標準入力から読み込んだ内容をEvernoteへ送るプログラムを書きました。
プログラム
# -*- coding: utf-8 -*- # ever_mail.py # ---- CONFIGURATIONS ---- EVERNOTE_MAIL_ADDRESS = 'evernote_mail_address' FROM_ADDRESS = 'from_address' SMTP_HOST = 'smtp._host' SMTP_PORT = 25 DEBUG = True # ---- CONFIGURATIONS ---- import datetime import email.header import optparse import smtplib import sys from email.mime.text import MIMEText class EverMailClient(object): def __init__(self, smtp): assert isinstance(smtp, smtplib.SMTP) self._smtp = smtp def send(self, msg): assert isinstance(msg, MIMEText) mail_from = msg['From'] mail_to = msg['To'] self._smtp.sendmail(mail_from, mail_to, msg.as_string()) class EverMailTitle(object): def __init__(self, title, notebook, tags): self._title = title if title else str(datetime.datetime.now) self._notebook = notebook if notebook else '' self._tags = tags if tags else [] def __str__(self): result = self._title if 0 < len(self._notebook): result += ' @' + self._notebook for tag in self._tags: result += ' #' + tag return result class EverMailOptionParser(optparse.OptionParser): def __init__(self): optparse.OptionParser.__init__(self) self.add_option('--notebook', '-n', action='store') self.add_option('--tag', '-t', action='append') def create_smtp_client(): smtp = smtplib.SMTP(SMTP_HOST, SMTP_PORT) if DEBUG: smtp.set_debuglevel(1) return smtp def create_message_from_stdin(title): assert isinstance(title, EverMailTitle) body = sys.stdin.read() msg = MIMEText(bytes(body)) msg['Subject'] = email.header.Header(str(title), 'utf-8') msg['From'] = FROM_ADDRESS msg['To'] = EVERNOTE_MAIL_ADDRESS msg.set_charset('utf-8') return msg if __name__ == '__main__': option_parser = EverMailOptionParser() (options, args) = option_parser.parse_args() note_title = args[0] if args and 0 < len(args) else '' title = EverMailTitle(note_title, options.notebook, options.tag) msg = create_message_from_stdin(title) smtp = None try: smtp = create_smtp_client() client = EverMailClient(smtp) client.send(msg) finally: if smtp: smtp.close()
使用例
$ cat your_important_file | python ever_mail.py TITLE -nNOTEBOOK -tTAG -tTAG
工夫したところは、optparseモジュールを使ってオプションの解析を実装したところです。
暇があれば、画像の添付なども対応したいと思います。
参考
15.5. optparse — コマンドラインオプション解析器
Eメールを送信するだけでEvernoteに簡単送信!
2014年3月3日月曜日
Pythonで単体テストをするには
「pythonでunittestをするには」にて、
テストプログラム
テストプログラムのディレクトリ構成とプログラムは以下のとおりです。
test/test1.py
test/test2.py
test/testsub/__init__.py
空のファイルです。testsubをモジュールとして認識させるために必要です。
test/testsub/testsub1.py
フォルダ配下のテストをすべて実行する
pythonの-mオプションのパラメータとしてunittestを渡します。詳細な出力を得るために、-vオプションも追加します。
モジュールを指定してテストする
上のオプションに加え、モジュール名を追加するとモジュールのテストになります。
クラスを指定してテストする
さらにクラス名を追加します。
テストメソッドを指定してテストする
さらにメソッド名を追加します。
テストスイートを指定してテストする
テストスイートを指定することもできます。要領はクラスやメソッドを指定する時と同様です。
一括して実行できるだけでなく、任意のテストメソッドを選択することもできるので、テンポよく開発が進められると思いました。
しかし、複数のテストファイルを作成した後でまとめてテストを実行する手段がないようなので、以下のようなコードを書いてみました。という記載がありますが、Python 2.6にてまとめてテストを実行する手段が追加されましたので、勝手にフォローさせていただきます。動作確認にはPython 3.3を使用しました。
テストプログラム
テストプログラムのディレクトリ構成とプログラムは以下のとおりです。
test ├test1.py ├test2.py └testsub ├__init__.py └testsub1.py
test/test1.py
# -*- coding: utf-8 -*- import unittest class TestCase1(unittest.TestCase): def test_success(self): pass def test_fail(self): self.fail() class TestCase2(unittest.TestCase): def test_success(self): pass def test_fail(self): self.fail() suite = unittest.TestSuite() suite.addTest(TestCase1('test_success')) suite.addTest(TestCase2('test_success'))
test/test2.py
# -*- coding: utf-8 -*- import unittest class TestCase1(unittest.TestCase): def test_success(self): pass def test_fail(self): self.fail()
test/testsub/__init__.py
空のファイルです。testsubをモジュールとして認識させるために必要です。
test/testsub/testsub1.py
# -*- coding: utf-8 -*- import unittest class TestCase1(unittest.TestCase): def test_success(self): pass def test_fail(self): self.fail()
フォルダ配下のテストをすべて実行する
pythonの-mオプションのパラメータとしてunittestを渡します。詳細な出力を得るために、-vオプションも追加します。
~/py/test $ python3 -m unittest -v test_fail (test1.TestCase1) ... FAIL test_success (test1.TestCase1) ... ok test_fail (test1.TestCase2) ... FAIL test_success (test1.TestCase2) ... ok test_fail (test2.TestCase1) ... FAIL test_success (test2.TestCase1) ... ok test_fail (testsub.testsub1.SubTestCase1) ... FAIL test_success (testsub.testsub1.SubTestCase1) ... ok ====================================================================== FAIL: test_fail (test1.TestCase1) ---------------------------------------------------------------------- Traceback (most recent call last): File "/cygdrive/c/home/myamamo/py/test/test1.py", line 10, in test_fail self.fail() AssertionError: None ====================================================================== FAIL: test_fail (test1.TestCase2) ---------------------------------------------------------------------- Traceback (most recent call last): File "/cygdrive/c/home/myamamo/py/test/test1.py", line 17, in test_fail self.fail() AssertionError: None ====================================================================== FAIL: test_fail (test2.TestCase1) ---------------------------------------------------------------------- Traceback (most recent call last): File "/cygdrive/c/home/myamamo/py/test/test2.py", line 10, in test_fail self.fail() AssertionError: None ====================================================================== FAIL: test_fail (testsub.testsub1.SubTestCase1) ---------------------------------------------------------------------- Traceback (most recent call last): File "/cygdrive/c/home/myamamo/py/test/testsub/testsub1.py", line 10, in test_fail self.fail() AssertionError: None ---------------------------------------------------------------------- Ran 8 tests in 0.004s FAILED (failures=4)
モジュールを指定してテストする
上のオプションに加え、モジュール名を追加するとモジュールのテストになります。
~/py/test $ python3 -m unittest -v test1 test_fail (test1.TestCase1) ... FAIL test_success (test1.TestCase1) ... ok test_fail (test1.TestCase2) ... FAIL test_success (test1.TestCase2) ... ok ====================================================================== FAIL: test_fail (test1.TestCase1) ---------------------------------------------------------------------- Traceback (most recent call last): File "test1.py", line 10, in test_fail self.fail() AssertionError: None ====================================================================== FAIL: test_fail (test1.TestCase2) ---------------------------------------------------------------------- Traceback (most recent call last): File "test1.py", line 17, in test_fail self.fail() AssertionError: None ---------------------------------------------------------------------- Ran 4 tests in 0.002s FAILED (failures=2)
クラスを指定してテストする
さらにクラス名を追加します。
~/py/test $ python3 -m unittest -v test1.TestCase1 test_fail (test1.TestCase1) ... FAIL test_success (test1.TestCase1) ... ok ====================================================================== FAIL: test_fail (test1.TestCase1) ---------------------------------------------------------------------- Traceback (most recent call last): File "test1.py", line 10, in test_fail self.fail() AssertionError: None ---------------------------------------------------------------------- Ran 2 tests in 0.001s FAILED (failures=1)
テストメソッドを指定してテストする
さらにメソッド名を追加します。
~/py/test $ python3 -m unittest -v test1.TestCase1.test_fail test_fail (test1.TestCase1) ... FAIL ====================================================================== FAIL: test_fail (test1.TestCase1) ---------------------------------------------------------------------- Traceback (most recent call last): File "test1.py", line 10, in test_fail self.fail() AssertionError: None ---------------------------------------------------------------------- Ran 1 test in 0.002s FAILED (failures=1)
テストスイートを指定してテストする
テストスイートを指定することもできます。要領はクラスやメソッドを指定する時と同様です。
~/py/test $ python3 -m unittest -v test1.suite test_success (test1.TestCase1) ... ok test_success (test1.TestCase2) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
一括して実行できるだけでなく、任意のテストメソッドを選択することもできるので、テンポよく開発が進められると思いました。
登録:
投稿 (Atom)