我有以下方法来设置与DB的连接并最终将其拆除。功能看起来像这样
def read_db(self, sql_statement):
conn = pymysql.connect(host=self.h,user=self.u,passwd=self.pw,
db=self.db,port=self.p)
try:
with conn.cursor() as cur:
cur.execute(sql_statement)
doStuffGeneratingException()
except Exception:
cur.rollback()
raise
finally:
conn.close()
现在如果我不得不用上下文管理器替换它,我认为它看起来像
@contextmanager
def setup_sql():
conn = pymysql.connect(host=self.h,user=self.u,passwd=self.pw,
db=self.db,port=self.p)
yield conn.cursor()
connection.cursor().close()
self.connection.close()
def read_db(self, sql_statement):
with setup_sql() as cur:
try:
cur.execute(sql_statement)
doStuffGeneratingException()
except:
cur.rollback()
raise
现在我的问题是
pymysql.connect(...)
内执行contextmanager
语句时发生错误,该如何处理?怎么会冒泡到调用函数? doStuffGeneratingException()
实施中的with
出现异常,会发生什么?控件是否会先setup_sql
执行yield
之后的语句? 答案 0 :(得分:1)
1,sorta。整个try / except需要另一级别的缩进。
def read_db(self, sql_statement):
with setup_sql() as cur:
try:
cur.execute(sql_statement)
doStuffGeneratingException()
except:
cur.rollback()
raise
2,代码中的任何地方都没有处理错误,因此python本身会报告异常并暂停执行。它可以在你选择的任何地方被捕获。在setup_sql()和read_db()内部都是可行的,但是如果你打算对它做些什么,通常你想要处理异常,尽可能接近提升它们的异常。要在read_db()中执行此操作,需要使用setup_sql()进行另一次try:block:
def read_db(self, sql_statement):
try:
with setup_sql() as cur:
try:
cur.execute(sql_statement)
doStuffGeneratingException()
except:
# gets exceptions thrown by cur.execute() and doStuffGeneratingException()
# will not process context manager statements after yield if flow doesn't continue in this function past this except block
cur.rollback()
raise
except:
# gets exceptions thrown by `with setup_sql() as cur:`
# again none of the statements within the context manager after the statement that raises an exception will be executed
pass
3,没有。一个例外是立即“返回”它会回滚,并且重新提升它会中止你的阻止。如果您希望上下文管理器完成,请捕获异常并处理它们而不重新提升。如果您需要在那里引发异常并希望上下文管理器完成其工作,请设置一个标志变量并在关闭上下文管理器后引发,或者以另一种方式重构代码以实现该目标。
答案 1 :(得分:0)
我相信您实际上可以通过在方法内部的 yield
语句周围放置 try / except / finally 来封装上下文管理器中的错误处理,例如:
from contextlib import contextmanager
@contextmanager
def setup_sql():
conn = pymysql.connect(host=self.h,user=self.u,passwd=self.pw,
db=self.db,port=self.p)
cursor = conn.cursor()
try:
yield cursor
except Exception:
cursor.rollback()
raise
finally:
cursor.close()
conn.close()
def read_db(self, sql_statement):
with setup_sql() as cur:
cur.execute(sql_statement)
doStuffGeneratingException()
我没有试过这个,但我发现 this comment on another SO question 链接到关于 @contextmanager 的文档,解释了它是如何工作的。