TIL

Today I Learned. 知ったこと、学んだことを書いていく

gzipモジュールを使った圧縮と展開 - Python

vcrpyでは、レスポンスの量が多い場合、圧縮されて(バイナリになって)カセットに格納される。どうやって圧縮されたデータを扱うのか調べてみたら標準モジュールのgzipモジュールでできそうだったため、メモしておく

圧縮にはgzip.compress()メソッド。展開にはgzip.decompress()メソッド。それぞれ、バイト配列を扱う。

文字列を圧縮して、展開して、取得してみた

>>> import gzip
>>> t_bytes = bytes('あいう123abc', 'utf-8')
>>> t_bytes
b'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86123abc'

>>> compressed_bytes = gzip.compress(t_bytes)    # 圧縮
>>> compressed_bytes
b'\x1f\x8b\x08\x00G::[\x02\xff{\xdc\xd8\xf4\xb8\xb1\xe5qc\x9b\xa1\x91qbR2\x00\xe1\xc7\x079\x0f\x00\x00\x00'

>>> decompressed_bytes = gzip.decompress(compressed_bytes)    # 展開
>>> decompressed_bytes
b'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86123abc'

>>> decompressed_bytes.decode('utf-8')
'あいう123abc'

gzip.compress()にはバイト配列(bytes)を渡さないといけないため、bytes(str, 'utf-8')でバイト配列を作成する

gzip.compress()に作成したバイト配列を渡して、圧縮する。

展開にはgzip.decompress()を使う。圧縮されているバイト配列を渡すことで、展開してくれる

展開したバイト配列をbytes.decode()で文字列に変換し、終了

これで、一応、圧縮と展開ができるようになった

参考文献

mypyとjedi-vimで補完を強くする - Vim

Pythonで型アノテーションを使いたくなって、どうせならVimでも補完できるようにしたいと思って、やってみた

Python3が有効になっているか確認

まずはVimでPython3が有効になっているか確認する
もし有効になっていない場合、ここを参考にして、有効にする

$ pip3 install -U flake8 mypy-lang jedi

neomakeを入れる

非同期で型、スタイルチェックを行うにはneomakeを使う

neomake/neomake: Asynchronous linting and make framework for Neovim/Vim

call dein#add('neomake/neomake')

...
let g:neomake_python_enabled_makers = ['python', 'flake8', 'mypy']

autocmd FileType python setlocal omnifunc=jedi#completions
if !exists('g:neocomplete#force_omni_input_patterns')
    let g:neocomplete#force_omni_input_patterns = {}
endif
let g:neocomplete#force_omni_input_patterns.python = '\%([^. \t]\.\|^\s*@\|^\s*from\s.\+import \|^\s*from \|^\s*import \)\w*'

syntasticを入れる

なんとなくsyntasticも入れた

vim-syntastic/syntastic: Syntax checking hacks for vim

call dein#add('scrooloose/syntastic')

...

let g:syntastic_check_on_open = 1
let g:syntastic_python_checkers = ['flake8', 'pep257', 'mypy']
let g:syntastic_python_flake8_args = '--max-line-length=120'

入れるときにエラーとかのメモ

以下の記述を書かないとエラーになった

if !exists('g:neocomplete#force_omni_input_patterns')
    let g:neocomplete#force_omni_input_patterns = {}
endif

こんなエラー

E121: Undefined variable: g:neocomplete#force_omni_input_patterns

あと、無効にしていたから、動作しなくて焦った

let g:jedi#completions_enabled = 0

jedi#completetions_enabledは、jedi-vimの補完を有効にするかどうかってことらしい。deoplete-jediを使うときにはこれを無効(0)にするらしい

参考文献

GETリクエストのパラメータの空白はそのままでいい requests - Python

requests.get()でGETリクエストを送信するときのパラメータに空白が入るときはどうすればいいのかわからなかったが、そのまま渡していいことがわかった

import requests

params = {'aaa': '111 あああ',
          'bbb': '222 いいい'}

res = requests.get('http://google.com', params=params)

リクエストされるURLは'http://google.com/?aaa=111+%E3%81%82%E3%81%82%E3%81%82&bbb=222+%E3%81%84%E3%81%84%E3%81%84'となる


requestsのソースを追っていったらなんかわかった気がするからメモしておく

パラメータがエンコードされるまでのスタックトレース(下から上に呼び出している)

urlencode() urllib.parse.py:906
_encode_params() models.py:105
prepare_url() [PreparedRequest] models.py:423
prepare() [PreparedRequest] models.py:305
prepare_request() [Session] sessions.py:437
request() [Session] sessions.py:494
request() api.py:58
get() api.py:72

urllib.parse.urlencode()が最終的に呼ばれていたため、' 'が'+'になっているということ!!あまり気にしなくても良かったのかも...

参考文献

github.com

requests.apiモジュール - Python

https://github.com/requests/requests/blob/00fd4c8eb4ac0fd7b8f8d76bbf15ab06351c052c/requests/api.py#L16-L58 から重要なところを引っ張ってきた

def request(method, url, **kwargs):
    with sessions.Session() as session:
        return session.request(method=method, url=url, **kwargs)

リクエストを送る前に毎回Sessionを生成しているということ。また、withを使ってるからそのセッションは閉じられている。セッションは必ず閉じないといけない。

使用例

セッションを使うときはrequests.Sessionを使い、セッションを使わないときはrequests.apiを使うみたいな?

import requests


class Requester(object):
    _session = None

    def __init__(self, requests_session=True):
        if requests_session:
            self._session = requests.Session()
        else:
            from requests import api
            self._session = api

    def internal_call(self):
        r = self._session.request('GET', 'http://google.com')
        print(r.status_code)

if __name__ == '__main__':
    requester = Requester(False)
    requester.internal_call()
    requester = Requester()
    requester.internal_call()

実行結果

200
200

参考文献

実行しているPythonのバージョンを取得 - Python

いくつかの方法があるらしい

  1. sys.hexversionを使う
  2. platform.python_version_tuple()を使う

1.sys.hexversionを使う

これはPyGithubのソースを見てたときに見つけて一人で感動した書き方

sys.hexversionを使うことでPythonのバージョンの16進数を取得できる。

>>> import sys
>>> sys.hexversion
50725872
>>> hex(sys.hexversion)
'0x30603f0'
>>> sys.version
'3.6.3 (default, Oct  4 2017, 06:09:15) \n[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.37)]'

「Python3以上のとき」のような処理をしたいときには0x03000000と比較すればいい

>>> sys.hexversion >= 0x03000000
True

この結果をatLeastPython3のように変数に入れておけば使える!!

2.platform.python_version_tuple()を使う

>>> import platform
>>> platform.python_version_tuple()
('3', '6', '3')
>>> int(platform.python_version_tuple()[0]) >= 3
True

なんか微妙...


sys.hexversionのほうが好き!!!!

参考文献

特定の行をハイライトした状態のリンクを作成 - GitHub

f:id:tmg1998:20180526093429p:plain:w500

こんな感じでハイライトした状態のリンクを張りたいとき

github.com

リンクの後ろに#L70-L72といったように行番号をつける。
また、コミットのファイルに対してのリンクにすることで必ずその行数になる。

https://github.com/tamago324/CasNewCommentNoticePy/blob/115ebcab2e1f2539e678ac3383b30d8a8b41324c/src/api/comment.py#L70-L72

参考文献

pydubを使って音楽を鳴らす - Python

Pythonで音楽(mp3)を鳴らすにはpydubというライブラリを使えば実現できる。

github.com


音楽鳴らすまでに行ったことはここに書いてある

音を鳴らす · Issue #2 · tamago324/CasNewCommentNoticePy

環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.13.3
BuildVersion:   17D102

音楽を鳴らすまでの手順

  1. ffmpegをインストール
  2. pipenv install pydubでpydubのインストール

pydubを使うにはffmegというソフトをインストールしなくてはいけない。これに少し手こずった

1.ffmpegをインストール

/usr/local/share/man/man3の権限を実行するユーザーにする必要がある

$ cd /usr/local/share/man/
$ sudo chown -R $USER man3

次にffmpegをインストールする

$ brew install ffmpeg --with-libvorbis --with-sdl2 --with-theora

インストールできたか確認

$ ffmpeg
ffmpeg version 4.0 Copyright (c) 2000-2018 the FFmpeg developers
  built with Apple LLVM version 9.0.0 (clang-900.0.39.2)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/4.0 --enable-shared --enable-pthreads --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-gpl --enable-ffplay --enable-libmp3lame --enable-libtheora --enable-libvorbis --enable-libx264 --enable-libxvid --enable-opencl --enable-videotoolbox --disable-lzma
  libavutil      56. 14.100 / 56. 14.100
  libavcodec     58. 18.100 / 58. 18.100
  libavformat    58. 12.100 / 58. 12.100
  libavdevice    58.  3.100 / 58.  3.100
  libavfilter     7. 16.100 /  7. 16.100
  libavresample   4.  0.  0 /  4.  0.  0
  libswscale      5.  1.100 /  5.  1.100
  libswresample   3.  1.100 /  3.  1.100
  libpostproc    55.  1.100 / 55.  1.100
Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...

Use -h to get full help or, even better, run 'man ffmpeg'

2.pipenv install pydubでpydubのインストール

普通にこれはインストールできた

$ pipenv install pydub

実行してみる

鳴らす音楽は以下のサイトからダウンロードしておいた

ボタン・システム音[1]|効果音ラボ

フォルダ構成は以下の通り

.
└── sound
    ├── decision7.mp3
    └── sound.py

ソース

import os

from pydub import AudioSegment
from pydub.playback import play

ABS_PATH = os.path.dirname(os.path.abspath(__file__))

def play_sound():
    sound = AudioSegment.from_mp3(f'{ABS_PATH}/decision7.mp3')
    play(sound)


if __name__ == '__main__':
    play_sound()

実行

$ pipenv run python3 sound/sound.py
Loading .env environment variables…
Input #0, wav, from '/var/folders/93/g9_qrz2519z_p3y0k44pctt00000gn/T/tmpp9zk38i8.wav':
  Duration: 00:00:00.86, bitrate: 1411 kb/s
    Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, 2 channels, s16, 1411 kb/s
   0.81 M-A:  0.000 fd=   0 aq=    0KB vq=    0KB sq=    0B f=0/0

音楽がなった!!これだけで音楽鳴らせるとかpydubに感謝!!

音楽プレイヤーとか作れるのかな...とか思ったりしてる

参考文献