with文

Python with 文のサンプルは、ネット検索するとたくさんあります。
クラスを用意、__enter__ と __exit__ で前処理、後処理を実行する方法

# -*- coding: UTF-8 -*-

class Foo():
    def __init__(self, msg):
        self.msg = msg
    def getMsg(self):
        return self.msg
    def __enter__(self):
        print('Foo __enter__')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('Foo __exit__')
        if exc_type:
            print("exc_type : %s" % exc_type)
            print("exc_val  : %s" % exc_val)
            print("exc_tb   : %s" % exc_tb)

with Foo('Hello') as f:
    print('test {}'.format(f.getMsg()))
    # print('%d' % 'a')

この例では、、
 __enter__で、return self により自身のインスタンスを返して
 メソッド getMsg() が実行できるようにします。
結果

Foo __enter__
test Hello
Foo __exit__

これに、例外を発生するように実行します。

with Foo('Hello') as f:
    print('test {}'.format(f.getMsg()))
    print('%d' % 'a')

例外を発生さえた結果

Foo __enter__
Traceback (most recent call last):
test Hello
Foo __exit__
exc_type : <class 'TypeError'>
exc_val  : %d format: a number is required, not str
exc_tb   : <traceback object at 0x0362FB70>

contextmanagerを使う方法
これは実行するメソッドがインターセプト対象になり、
前処理、後処理を、yeild 前後で書くことになります。
例外は、try:~except で捕捉する処理を書きます。

from contextlib import contextmanager
from traceback import format_exception

@contextmanager
def execute(msg):
    try:
        print('# 前処理')
        yield msg.upper()
        print('# 後処理')
    except BaseException as e:
        print(''.join(format_exception(type(e), e, e.__traceback__)))
    finally:
        print('# finally')

with execute('Hello') as e:
    print('-- start')
    print(e)
    print('-- end')

yield で、値を返すこともできます。
結果

# 前処理
-- start
HELLO
-- end
# 後処理
# finally

これに、例外を発生するように実行します。

with execute('Hello') as e:
    print('-- start')
    print(e)
    print('%d' % 'a')

例外を発生さえた結果

# 前処理
-- start
HELLO
Traceback (most recent call last):
  File "C:/Users/xxx/PycharmProjects/xxxx/test.py", line 9, in execute
    yield msg.upper()
  File "C:/Users/xxx/PycharmProjects/xxx/test.py", line 18, in <module>
    print('%d' % 'a')
TypeError: %d format: a number is required, not str
# finally

例外捕捉を以下のように書きましたが、

    except BaseException as e:
        print(''.join(format_exception(type(e), e, e.__traceback__)))

次のように書いても同じです。

    except BaseException as e:
        for v in format_exception(type(e), e, e.__traceback__):
            print(v.rstrip())

この contextmanager の方法を踏まえて、過去に書いた
oboe2uran.hatenablog.com

に当てはめて書くと、、、

from contextlib import contextmanager
from traceback import format_exception
@contextmanager
def query(statement):
    try:
        import mysql.connector
        connect = mysql.connector.connect(host='localhost', port=3306, user='food', password='a1B01a', database='foodb', charset="utf8")
        cur = connect.cursor()
        cur.execute(statement)
        table = cur.fetchall()
        yield table
    except BaseException as e:
        print(''.join(format_exception(type(e), e, e.__traceback__)))
    finally:
        connect.close()

with query('SELECT * FROM users') as res:
    print(res)

で、結果 res は、1レコードはタプルでSELECT全行の結果としてリストで返ってきます。

クエリメソッドではなく、コネクション接続後のカーソルにする場合は、、

from contextlib import contextmanager
from traceback import format_exception
@contextmanager
def connect():
    try:
        import mysql.connector
        connect = mysql.connector.connect(host='localhost', port=3306, user='food', password='a1B01a', database='foodb', charset="utf8")
        cur = connect.cursor()
        yield cur
    except BaseException as e:
        print(''.join(format_exception(type(e), e, e.__traceback__)))
    finally:
        connect.close()

with connect() as cur:
    cur.execute("SELECT * FROM users")
    table = cur.fetchall()
    print(table)

トランザクションの為に書けば、、
commit roleback を差し込んで以下のように書けますね。

from contextlib import contextmanager
from traceback import format_exception
@contextmanager
def connect():
    try:
        import mysql.connector
        connect = mysql.connector.connect(host='localhost', port=3306, user='food', password='a1B01a', database='foodb', charset="utf8")
        cur = connect.cursor()
        yield cur
        connect.commit
    except BaseException as e:
        connect.rollback()
        print(''.join(format_exception(type(e), e, e.__traceback__)))
    finally:
        connect.close()