Pythonのサンプル集

## 実行中のスクリプトのパスを取得
### 相対パス

```py
# -*- coding:utf-8 -*-
import os
print(os.path.dirname(__file__))
```

### 絶対パス

```py
# -*- coding:utf-8 -*-
import os.path
print(os.path.dirname(os.path.abspath(__file__)))
```

## Logger

```py
# -*- coding:utf-8 -*-
import logging
from logging import getLogger, FileHandler, StreamHandler, Formatter
import datetime


def my_logger(modname, log_filename=None, dry_run=False, log_format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'):
    '''
    ログ出力フォーマット設定
    '''
    handler_format = Formatter(log_format)

    logger = getLogger(modname)

    '''
    loggerのログレベル設定(ハンドラに渡すエラーメッセージのレベル)
    '''
    logger.parent.setLevel(logging.DEBUG)

    '''
    ファイル出力
    dry_run = Trueのときにはファイル出力しないようにする
    '''
    if log_filename and not dry_run:
        file_handler = FileHandler(log_filename)
        file_handler.setLevel(logging.DEBUG)
        file_handler.setFormatter(handler_format)
        logger.addHandler(file_handler)

    '''
    標準出力
    '''
    stream_handler = StreamHandler()
    stream_handler.setLevel(logging.DEBUG)
    stream_handler.setFormatter(handler_format)
    logger.addHandler(stream_handler)

    return logger

if __name__ == '__main__':
    my_logger = my_logger('main', '/var/log/sample.log')
    my_logger.debug('This is a debug log')
    my_logger.info('This is a info log')
    my_logger.warn('This is a warn log')
    my_logger.error('This is a error log')
    my_logger.critical('This is a critical log')

    '''
    2019-02-15 17:04:13,673 - main - DEBUG - This is a debug log
    2019-02-15 17:04:13,674 - main - INFO - This is a info log
    2019-02-15 17:04:13,674 - main - WARNING - This is a warn log
    2019-02-15 17:04:13,674 - main - ERROR - This is a error log
    2019-02-15 17:04:13,674 - main - CRITICAL - This is a critical log
    '''
```


## requestsでタイムアウト処理

```py
# -*- coding:utf-8 -*-
import requests
from requests.packages.urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter

s = requests.Session()

'''
totalで5回リトライをする
backoff_factor=1の場合、リトライ間隔は、1回目は1秒、2回目は2秒、3回目は3秒・・・
status_forcelistで定義されたstatusのときのみリトライする
'''
retries = Retry(total=5,
                backoff_factor=1,
                status_forcelist=[ 500, 502, 503, 504 ])

s.mount('http://', HTTPAdapter(max_retries=retries))
s.mount('https://', HTTPAdapter(max_retries=retries))

'''
connection timeoutが10秒、read timeoutが30秒
'''
r = s.get('https://example.com/', timeout=(10.0, 30.0))
r.raise_for_status()

'''
response bodyを表示
'''
print(r.text)

'''
status codeを表示
'''
print(r.status_code)
```


## シェルコマンド実行

```py
# -*- coding:utf-8 -*-
import subprocess

proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = proc.communicate()

print(proc.returncode)
print(stdout)
print(stderr)
```

### subprocessで実行結果の標準出力をリアルタイムで表示する

```py
import os
import sys
import subprocess

def run(cmd):
    proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    while True:
        line = proc.stdout.readline()
        if line:
            yield line

        if not line and proc.poll() is not None:
            break

def main():
    for line in run('ls -l /'):
        sys.stdout.buffer.write(line)

if __name__ == '__main__' :
    main()
```

## 正規表現エスケープ

```py
import re
re.escape('エスケープしたい文字列')
```

## Template
テンプレートを作成

```py:sample.tpl
Subject: ${subject}
Message: ${message}
```

テンプレートを元にファイルを生成する

```py
# -*- coding:utf-8 -*-
from string import Template

template_file = 'sample.tpl'
t = Template(unicode(open(template_file).read(), 'utf-8', 'ignore'))
text = t.substitute({'subject' : 'タイトル', 'message' : 'メッセージ'})

print(text)

'''
Subject: タイトル
Message: メッセージ
'''
```

## Pythonでシェルコマンドを実行する
subprocess

```py:sample
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import subprocess

def execute_cmd(cmd):
    proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    stdout, stderr = proc.communicate()

    return {'returncode': proc.returncode, 'stdout': stdout, 'stderr': stderr}

if __name__ == '__main__':
    # 実行
    cmd = 'whoami'
    res = execute_cmd(cmd)
    print(res)

    '''
    # 実行結果
    {'returncode': 0, 'stderr': None, 'stdout': 'username\n'}
    '''
```

リアルタイムで標準出力を取得する

```py
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import subprocess
import os
import sys

def execute_cmd(cmd):
    proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    while True:
        line = proc.stdout.readline()
        if line:
            yield line
        if not line and proc.poll() is not None:
            break

if __name__ == '__main__':
    cmd = 'top'
    for line in execute_cmd(cmd):
        sys.stdout.write(line)
```

## Pythonでオプション付きのコマンドを作成する(argparse)
- [Pythonでオプション付きのコマンドを作成する(argparse)](https://qiita.com/Esfahan/items/72b3c34d744eaaea6c93)

## 自作モジュール内でlogを出力する
- [【Python】自作モジュール内でloggingする](https://qiita.com/Esfahan/items/275b0f124369ccf8cf18)

## Abstract with NotImplementedError for python3
SampleAbstractクラスで定義している`must_be_implemented_method001`というメソッドは、継承先のクラスで必ず実装されていなければならない。

```py:sample_class.py
# -*- coding:utf-8 -*-
from abc import ABCMeta, abstractmethod


class SampleAbstract(object, metaclass=ABCMeta):

    @abstractmethod
    def must_be_implemented_method001(self):
        raise NotImplementedError()


class Sample(SampleAbstract):

    def must_be_implemented_method001(self):
        pass

    def main(self):
        pass

if __name__ == '__main__':
    Sample()
```

もし継承先のSampleクラスで`must_be_implemented_method001`メソッドを実装せずに実行した場合、以下のエラーが発生する。

```py
$ python sample_class.py
Traceback (most recent call last):
  File "sample_class.py", line 18, in <module>
    sample = Sample()
TypeError: Can't instantiate abstract class Sample with abstract methods must_be_implemented_method001
```


参考: https://note.nkmk.me/python-datetime-isoformat-fromisoformat/

## datetime変換
UTC -> JSTに変換して、datetime型 -> string型に変換する。

```
$ pip install pytz
```

```py
from pytz import timezone

# UTC -> JST
datetime_jst = timezone('Asia/Tokyo').localize(datetime_utc)
# datetime -> string
datetime_jst.strftime('%Y-%m-%d %H:%M:%S')
```

## pyc -> pyに逆コンパイル
Python3で動きます。

```
$ pip install uncompyle6
```

sample.pycを逆コンパイル

```
$ uncompyle6 sample.pyc > sample.py
```