Pythonでjsonを読み込み、出力する際にdateやdatetime型を使用する
出力の時の話はよく記載がありましたが、読み込みの際に変換する方法はあまりなかったのでメモ。
概要
jsonでは日付型というのは定義されていません。(そもそもどう表現する?) そのため、pythonでjsonを読み込みの際に日付が含まれていても文字列になりますし、出力の際にdate型が含まれているとエラーになります。
対応方法としては読み込むためのloadsではobject_hook、出力のためのdumpsはdefaultというオプションがあるので、こちらを設定することで対応が可能です。
対応前のコード
import json data = '{"date" : "2019/02/08"}' d = json.loads(data) type(d['date']) # str
import json from datetime import datetime data = {'date' : datetime.now()} json.dumps(data) # TypeError: Object of type 'datetime' is not JSON serializable
対応後
詳しくは下記のコードを見てください。 出力時は型を調べて文字列に変換するだけなので何の問題もないですが、読み込み時は何らかの条件で日付を判別しdate型に変換しています。 (jsonには明示的に日付を示す構文はないためしょうがない。) ここではキーの末尾が「date」の場合に、date型に変換するようにしています。
import re import json from datetime import datetime, date DATE_FORMAT = '%Y/%m/%d' DATE_KEY = re.compile(r'date$') def _json_parser(dct): for k, v in dct.items(): if re.search(DATE_KEY, k): dct[k] = datetime.strptime(v, DATE_FORMAT).date() return dct json_str = '{"hoge_date" : "2019/01/02", "l" : [{"huga_date" : "2018/12/31"}]}' dct = json.loads(json_str, object_hook=_json_parser) print(dct) print(type(dct['hoge_date'])) # <class 'datetime.date'> print(type(dct['l'][0]['huga_date'])) # <class 'datetime.date'> def json_serial(obj): if isinstance(obj, date): return obj.strftime(DATE_FORMAT) return obj json.dumps(dct, default=json_serial)
参考
S3cmdのProxy設定について
s3cmdを使用して動いていたバッチ処理で、proxy設定を追加しようとしたら色々大変だったお話。
S3cmdとは
公式ページより。
Windows用にはS3Expressというのがあるらしい。
S3Express : Command Line S3 Client and S3 Backup for Windows
まとめ
- 環境変数(http_proxy)ではなく、設定ファイルに記載する
- proxy_host, proxy_port
- 設定ファイルのデフォルトは「~/.s3cfg」
- コマンド実行時に「-c」で設定ファイルを渡すことも可能
- ↑のproxy設定はPython2.7以上が必要
- Python2のバージョン上げたくない場合はPython3を使用することも可能
- S3cmdのV2以降はPython3を使用することも可能。
- python3を使用する場合にはs3cmdを直接実行するのではなく、「python3 s3cmd」的な感じで実行する必要がある。
経緯とか(読む意味なし)
- 会社でproxyを設定しろよという連絡がきた
- (半)自動化されていたとあるシステムが動かない
- S3cmdの処理で落ちている
Problem: error: [Errno 111] Connection refused
- Proxy設定してないのでは?
- export http_proxy=xxxxx
- export https_proxy=yyyy
- 変わらずエラー
- 調べた → 設定ファイルに書く必要がある
- 設定ファイルに書いた
- python2.7以上で動かせと怒られる
- 2.6.6か何かで動いていた
- python2のバージョンは上げたくない。というかpython3にしたい
- s3cmdは2のみ → s3cmdバージョン2で対応しているらしい → s3cmdのバージョンアップ
- python3をデフォルトにしてs3cmd起動
- python2.7以上で動かせと怒られる
- (゚Д゚)ハァ?
- シバンにpython2が指定してある
- python3に引数でs3cmd渡して起動
- OK
参考URL
Pythonからs3fsを使用してS3を操作した際にハマった件について
はじめに
PythonsからS3をいじる際に、これまではaws cliを直接叩いていたのですが、s3fsを使用するとexistsやlsみたいなわかりやすい名称で使えるというのをどこかで知ったので使ってみた。
同名でS3をマウントするもの( https://github.com/s3fs-fuse/s3fs-fuse )もあるようですが、本記事でのs3fs( https://github.com/dask/s3fs )とは別物です。 で、キャッシュのせいかファイルを置いた直後にexistsするとFalseだったりと整合性がとれなくてハマった話です。
s3fsとは
リポジトリTOPには下記のように記載されています。
S3FS builds on boto3 to provide a convenient Python filesystem interface for S3.
Google翻訳すると↓
「はじめに」にも書きましたが、同名でS3をマウントするものもあるようですが、別物です。 むしろ、GitHubの☆を見てもマウントする奴が3000以上、本題の方が130位なので、どちらかというとこの記事に書くライブラリの方が無名。
使い方
インストールして(詳細)
pip install s3fs
以下のように書けばOK
import s3fs fs = s3fs.S3FileSystem(anon=True) list = fs.ls('my-bucket')
ドキュメントがしっかりしているので、以下に気をつければ?簡単に使えて良いです。 が、気をつけるなんて現実的ではないので個人的にはあまりオススメしません。
ハマったことと対応方法
内容としては、ファイル配置後に上位のフォルダを見た際にFileNotFoundErrorになるという事。
で、キャッシュが悪さしているので、キャッシュを更新すれば正しく動作します。
(キャッシュ更新するにはlsのrefreshオプションをTrueにする)
具体的には下記のコード参照。 (実際に試したい場合には↓↓に全コード書いたので、そちらをオススメ。)
import s3fs fs = s3fs.S3FileSystem(key=key, secret=secret) # 特定のPATHの下にフォルダを挟んでファイルを配置 output_path = '{}/hoge/{}'.format(s3_path, 'test.txt') fs.put('./test.txt', output_path) # ファイルを配置したフォルダではなく上位のフォルダでlsするとFileNotFoundError try: fs.ls(s3_path) # FileNotFoundError except FileNotFoundError: print('ls : FileNotFoundError') # existsでも同様に取得できない if not fs.exists(s3_path): print('exists : False') # 上位のフォルダから再帰的に削除しようとしてもFileNotFoundError try: fs.rm(s3_path, recursive=True) # FileNotFoundError except FileNotFoundError: print('rm : FileNotFoundError') # lsにrefreshオプションがあったので付与すると取得できる try: fs.ls(s3_path, refresh=True) # refresh=Trueをつける print('ls : refreshしたら例外なし') except FileNotFoundError: pass # 1度refreshするとexistsも問題なし if fs.exists(s3_path): print('exists : refresh後はTrue') # 同様にrefresh後はlsにrefreshつけなくても例外なし try: fs.ls(s3_path) print('ls : FileNotFoundErrorは発生しない') except FileNotFoundError: pass
考察とか
- rmは存在確認でexistsを呼び出して、existsはlsをrefreshなしで呼び出しています。
- なので、挙動を変更したければforkしてexistsのls呼び出している所にrefresh=TrueとすればOK。
- そもそもキャッシュを使用しないようにする方法はなさそう。
- 使用しているライブラリboto3側で設定ができそうですが未調査。
- そもそもS3はフォルダという概念はなくて格納PATHはただのKEY。
- 参考 : Amazon S3における「フォルダ」という幻想をぶち壊し、その実体を明らかにする)
- なので、途中のPATHではFileNotFoundなのもまぁ間違いではない気もしなくもない。が、直感的ではない。
全コード
import s3fs key = 'xxxxxxxxxx' secret = 'yyyyyyyyyyyyyyy' s3_path = 'my-bucket/aaa/bbb' # 前準備 try: fs.ls(s3_path, refresh=True) fs.rm(s3_path, recursive=True) except FileNotFoundError: pass try: fs.ls(s3_path) except FileNotFoundError: pass try: fs.ls('{}/{}'.format(s3_path, 'hoge')) except FileNotFoundError: pass # 特定のPATHの下にフォルダを挟んでファイルを配置 output_path = '{}/hoge/{}'.format(s3_path, 'test.txt') fs.put('./test.txt', output_path) # ファイルが置かれていることを確認 try: fs.ls(output_path) print('ls : FileNotFoundErrorは発生しない') except FileNotFoundError: pass if fs.exists(output_path): print('exists : True') # ここから本番 # ファイルを配置したフォルダではなく上位のフォルダでlsするとFileNotFoundError try: fs.ls(s3_path) # FileNotFoundError except FileNotFoundError: print('ls : FileNotFoundError') # existsでも同様に取得できない if not fs.exists(s3_path): print('exists : False') # 上位のフォルダから再帰的に削除しようとしてもFileNotFoundError try: fs.rm(s3_path, recursive=True) # FileNotFoundError except FileNotFoundError: print('rm : FileNotFoundError') # lsにrefreshオプションがあったので付与すると取得できる try: fs.ls(s3_path, refresh=True) # refresh=Trueをつける print('ls : refreshしたら例外なし') except FileNotFoundError: pass # 1度refreshするとexistsも問題なし if fs.exists(s3_path): print('exists : refresh後はTrue') # 同様にrefresh後はlsにrefreshつけなくても例外なし try: fs.ls(s3_path) print('ls : FileNotFoundErrorは発生しない') except FileNotFoundError: pass
参考URL
PowerShellでJava実行する際にシステムプロパティを設定する際の注意点
概要
- PowerShellで
java -Dsystem.language=ja Sample
みたいな形でシステムプロパティを指定して実行すると正しく実行されない。- コマンドプロンプトなら勿論実行可能。
正しく実行する際には
java "-Dsystem.language=ja" Sample
のようにダブルクォーテーションで囲う必要がある。PowerShellは「-」の後に「.」が来ると区切りと判定するらしい
経緯とか
- 日本語版Stack Overflowで質問発見。
- ↓のJavaコマンドを投げても正しく動かないらしい。
java -cp libtensorflow-1.11.0.jar;. -Djava.library.path=.\jni HelloTensorFlow
- エラーメッセージを見るとクラスが正しく認識でされてないっぽいので、コマンドのどこかにスペース入ってるんじゃね?っと思ったら違うらしい。
- エラーメッセージ :
エラー: メイン・クラス.library.path=.\jniが見つからなかったかロードできませんでした
- エラーメッセージ :
- やり取りしていたら、PowerShell上ではオプションが正しく設定されないという事が発覚。
- 調べてみたらPowerShellの仕様っぽい。
echoの例
- 正常系1
PS C:\work\20181108> echo a b a b
- 正常系2
PS C:\work\20181108> echo a.b a.b
- 想定外1
PS C:\work\20181108> echo -a.b -a .b
- 想定外2
本題ではないですが、「,」を入れてみたら分割されました。
PS C:\work\20181108\j> echo a,b a b
参考URL
- Why does PowerShell split arguments containing hyphens and periods?
- 本家Stack Overflow
- period in arguments
- MicrosoftのQAサイト?
- PowerShellコマンド環境で引っかかったこと
- 日本語情報(↑↑のStack Overflowにたどり着いています)
S3で静的ファイルのホスティングしてIP制御
経緯
静的なHTMLをお客さんに見せる事になり、ローカルPCで見せてもいいけど、どうせならどこかにホスティングして置いといた方がいいよね。って事でS3に置いたらIP制御でハマったのでメモ。
※ハマったというか、エンドポイントではなくて直接ファイルにアクセスしてたのでうまくいかなかっただけ。。。
手順
- S3にバケット作る
- バケットのプロパティ → Static website hosting → 「このバケットを使用してウェブサイトをホストする」 → 保存
- エンドポイントのURLをメモ
http://bucket-name.s3-website-ap-northeast-1.amazonaws.com
- 2で設定したインデックスドキュメント(デフォルトはindex.html)を置く
- エンドポイントにブラウザでアクセスして、index.htmlが表示されることを確認。
- バケット → アクセス権限 → バケットポリシー
- 以下のように記載(うまくいかない場合は、ポリシージェネレーターで作成)
バケットポリシーエディタ
{ "Version": "xxxxxxxxxxxxx", "Id": "xxxxxxxxxxxx", "Statement": [ { "Sid": "xxxxxxxxxxxxx", "Effect": "Allow", "Principal": "*", "Action": "s3:*", "Resource": "arn:aws:s3:::bucket-name/*", "Condition": { "IpAddress": { "aws:SourceIp": [ "198.51.100.0", "192.0.2.0" ] } } } ] }
確認
- 自分のIPのみを設定して表示されることを確認。
- 他のIP(スマホからなど)で表示されないことを確認。
- ↑と同じく、直接ファイルのリンクにアクセスして、表示される、されない事を確認。
VSCodeで静的コード解析ツールPylintを使用する
はじめに
PythonではPEP8というコード規約が一般的に使用されているようです。(PEP 8 -- Style Guide for Python Code)
この規約に準拠したツールがいくつかあり、これ!っというものは特にないという印象です。
で、代表的なものとして、同名のpep8があるのでややこしい。
ちなみにツールの方のpep8は名前を変更してpycodestyleとなっていますので注意。(pep8は1.7.1が最終更新のようです。)
詳細はQiitaにまとまっている記事がありましたのでこちらを参照してください。(pep8 が pycodestyle に変わった話)
他にもflake8や今回紹介するPylintが競合としてあるようです。
で、今回はPylintを使用することになっていたので、こちらをVSCodeで使用する方法について調べたメモです。
手順
- VSCodeでPythonを入れる際にはまず使用するであろう拡張機能ms-python.pythonを入れる
- 設定を確認
"python.linting.enabled"
がtrue
になっていること"python.linting.pylintEnabled"
がtrue
になっていること"python.linting.lintOnSave"
はお好みで"python.linting.pylintPath"
このPATHでコマンドが実行されるので、PATHが通ってない場合には修正が必要です。
- Pylintの設定ファイルを作成
- デフォルトだと警告が出てくれない事、厳しすぎる規約を変更したい事から設定ファイルを作成します。
- 下記のコマンドでデフォルトの設定ファイルが作成されます。
pylint --generate-rcfile > .pylintrc
- 設定ファイルの場所は公式ドキュメントを参照。
- workingdirectoryにpylntrcか、.pylintrcを置いておけばよいかと。
- 日本語onlyの人は訳はここにありました
- 指摘を確認
- 問題ビューに指摘が表示されます。(macだとshift + command + m)
- 明らかにおかしいコードで表示されない場合には、設定ファイルを一旦renameして指摘されるか確認しましょう。
- 設定ファイルを調整
- システム全体で警告を無効化する場合は設定ファイルを修正。
- あるコードのみ例外的に無視したい場合にはコメントとして↓のように無視したいコードを入力すれば無視してくれます。
# pylint: disable=C0330
- あまり調べていないのですがファイル全体で無効化されるようなので注意。
- 好みですが、私は以下を修正しました。
- max-line-lengthを120
- デフォルトの100だと厳しいと感じました。
- good-namesにf,vを追加
- スコープに関係なく、1から2文字の変数を許してくれません。
- 実際のプロジェクトでは分析でよく使用される変数名を追加しています。
- あるコードのみ例外的に無視したい場合にはコメントとして↓のように無視したいコードを入力すれば無視してくれます。
参考URL
オブジェクトの配列をPandasのDataFrameに変換する
背景とか
オブジェクト(エンティティとか、JavaBeansとか、DTOとか呼ばれる属性(フィールド)しか持たない奴)の配列をPandasのDataFrameに変更したメモです。
簡単にできそうだったけどできなかったので結局dictionaryに変換して突っ込みました。
PythonにもPandasにも詳しくないのですが、こういうケースはよくあると思ったのですが、あまりない??
コード
import pandas as pd class Hoge: c1 = '' c2 = '' c3 = '' def __init__(self, *args): self.c1 = args[0] self.c2 = args[1] self.c3 = args[2] hoge1 = Hoge('c1_1','c2_1','c3_1') hoge2 = Hoge('c1_2','c2_2','c3_2') hoge3 = Hoge('c1_3','c2_3','c3_3') ar = [hoge1, hoge2, hoge3] # 最初こんなのでできるかなと思ってました。 df1 = pd.DataFrame(ar) print(df1) # 1列3行でHogeオブジェクトが設定される(当たり前) # 最終的にこうなりました。 df2 = pd.DataFrame(list(map(lambda hoge: vars(hoge), ar))) print(df2)