pythonで7-digitsなマイクロ秒を含んだ文字列をstriptimeでdatetimeオブジェクトにしようとしてハマった話
時刻を文字列表現にするときにマイクロ秒を含んだ表現の場合がある。例えばgithub actionsのログは以下のようなもの。
lint Set up job 2021-05-25T20:31:24.8848799Z Current runner version: '2.278.0'
2021-05-25T20:31:24.8848799Z
部分が時刻の表現。実はマイクロ秒部分が7-digitsなのだけれど、これが意図しない表現でハマってしまったと言う話。
%f Microsecond as a decimal number, zero-padded on the left. 000000, 000001, …, 999999 (5)
ここを参考に以下のようにすればdatetimeオブジェクトが手に入ると思ったがValueErrorになる。辛い。
>>> from datetime import datetime >>> s = "2021-05-25T20:31:24.8848799Z" >>> datetime.strptime(s, "%Y-%m-%dT%H:%M:%S.%f%z") ... ValueError: time data '2021-05-25T20:31:24.8848799Z' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
詳細
まずその前にマイクロ秒部分を除いた表現で試してみる。
>>> datetime.strptime("2021-05-25T20:31:24", "%Y-%m-%dT%H:%M:%S") datetime.datetime(2021, 5, 25, 20, 31, 24)
これは上手く行く。
しかし、マイクロ秒部分を追加したらValueErrorになった。辛い。実は先程引用したドキュメントにそのあたりの説明が書かれてはいる。
When used with the strptime() method, the %f directive accepts from one to six digits and zero pads on the right. %f is an extension to the set of format characters in the C standard (but implemented separately in datetime objects, and therefore always available).
試しに6-digitsでやってみる。
>>> s[:26] '2021-05-25T20:31:24.884879' >>> datetime.strptime(s[:26]+s[27:], "%Y-%m-%dT%H:%M:%S.%f%z") datetime.datetime(2021, 5, 25, 20, 31, 24, 884879, tzinfo=datetime.timezone.utc)
アドホックなコードになってしまったがたしかに取れた。しかしだるい。
秒数部分で区切っても良いが、時刻部分は切り貼りせずそのまま使いたい。
>>> s.split(".")[0] '2021-05-25T20:31:24' >>> len(s.split(".")[0]) 19 >>> datetime.strptime(s[:19], "%Y-%m-%dT%H:%M:%S") datetime.datetime(2021, 5, 25, 20, 31, 24)
標準ライブラリでこの辺サポートしてくれると助かるのだけれど。
pendulum
pendulumを使っている場合はその辺をよしなにやってくれる1。
>>> import pendulum >>> pendulum.parse(s) DateTime(2021, 5, 25, 20, 31, 24, 884879, tzinfo=Timezone('UTC'))
が、依存を増やしたくない場合もあったりはする。
例えばアプリケーションコードの場合はbundleしたりdeployすれば良いだけなのであまり困らないのだけれど、色んな所に配布したいようなコードの場合に依存を増やさず1ファイルで収まると都合が良い場合が多い。これはgoでワンバイナリが手軽というのと同じような話かもしれない(書き手側の苦労でこの恩恵を得るための要求を満たそうとしている)。
そういう意味ではユースケースに応じてコードの書き方も変わるよなーというようなことを最近良く思っている2
追記
pythonのソースコード自体を書き換えれば結構手軽に対応できるようだ。
datetime.datetime.strptimeは_strptime
モジュールをimportして使っているだけ。
テキトーに変更してあげれば動く。
https://gist.github.com/podhmo/1cb1807f0207112735ea9d7dd8ca8836/revisions
goやpythonで複数のパッケージをmonorepo的に1つのrepositoryで管理する方法について調べていた
調べていたのでメモ。作業結果は以下のリポジトリにある。
どういうときに欲しくなるか?
どういうときに欲しくなるかと言うと、複数の関心事のそれぞれに対して複数の環境への共通の対応をしたくなったとき。その上その複数の関心事というのが曖昧な場合。
例えば、micro serviceのためのlog基盤を考えてみる。この場合にlogの出力の表現は同様の形式でハンドリングできるような環境だと嬉しい。 ここで、そのようにほしい機能や要件が決まっている場合はgo-logger,python-loggerのように別途repositoryを分けて管理すれば良いかもしれない。 (このように分けた場合にmasterデータや共通の仕様をどこに置くべきか?みたいな話も別途発生しうる)
ただ、その上、この欲しい機能のような何かが未確定で曖昧な状況になっているときにとりあえず1つのrepositoryで管理できるような状況だと嬉しい。 それこそ機能Mかつ言語MでMNだけのrepositoryを作ると言うようなことは避けたいというようなタイミングのとき。
どういうものが欲しいか?
とりあえず以下のことができれば良い
- 言語ごとにsub packageを切ってそれをpublicにinstallできる形で公開
- (それぞれのsub package毎のCI)
後者はまだ調べている途中。今回は前者を調べた。また前者は公式のpackage registry (e.g. pypi) などに登録する必要はない。
python
pythonの場合は、pipでインストールするときに、 subdirectory=<dir name>
というquery stringをつければ良いようだ。このような感じ。
If your repository layout is:
pkg_dir ├── setup.py # setup.py for package "pkg" └── some_module.py other_dir └── some_file some_other_file
Then, to install from this repository, the syntax would be:
$ python -m pip install -e "vcs+protocol://repo_url/#egg=pkg&subdirectory=pkg_dir"
例
利用例はこちら。foo,barというパッケージを使っている。
$ .venv/bin/python main.py hello from foo (0.1.1) hello from bar (0.1.1)
このパッケージ自体はpoetryを使って雑に作った。
- foo https://github.com/podhmo/multi-package-example/tree/main/python/foo
- bar https://github.com/podhmo/multi-package-example/tree/main/python/bar
requirements.txtの指定が肝。
main.py
import foo import bar foo.hello() bar.hello()
go
goでも同様にgo/foo,go/barというディレクトリ以下にパッケージを作る。
こちらは公開のために <directory name>v<version number>
というtagを付けて上げれば良いようだ。
For example, suppose we have a module example.com/repo/sub/v2, and we want to publish version v2.1.6. The repository root corresponds to example.com/repo, and the module is defined in sub/v2/go.mod within the repository. The prefix for this module is sub/. The full tag for this release should be sub/v2.1.6.
利用例
利用例はこちら1
$ go run main.go hello from foo (0.1.1) hello from bar (0.1.1)
パッケージはこちら
- foo https://github.com/podhmo/multi-package-example/tree/main/go/foo
- bar https://github.com/podhmo/multi-package-example/tree/main/go/bar
こちらの肝はこのMakefile