V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
bwangel
V2EX  ›  问与答

求助: TorMySQL 忽略异常

  •  
  •   bwangel · 2016-10-04 21:42:46 +08:00 · 1981 次点击
    这是一个创建于 3001 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天在用 TorMySQL 的时候遇到了一个问题,就是无法忽略表不存在的异常:

    问题描述

    我的代码如下:

    class Application(tornado.web.Application):
        def __init__(self):
            handlers = [
                (r"/join", RegisterHandler),
                (r"/", MainHandler),
                (r"/websocket", EchoWebSocket),
            ]
            settings = dict(
                cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE_",
                template_path=os.path.join(os.path.dirname(__file__), "templates"),
                static_path=os.path.join(os.path.dirname(__file__), "static"),
                xsrf_cookie=True,
                debug=True,
                schema=os.path.join(os.path.dirname(__file__), "schema.sql"),
            )
            self.pool = tormysql.ConnectionPool(
                max_connections = 20,
                idle_seconds = 7200,
                wait_connection_timeout = 3,
                host = "127.0.0.1",
                user = "chat",
                password = "chat",
                db = "chat",
                charset = "utf8"
            )
            super(Application, self).__init__(handlers, **settings)
            self.may_create_db()
    
        @gen.coroutine
        def may_create_db(self):
            with (yield self.pool.Connection()) as conn:
                try:
                    with conn.cursor() as cursor:
                        yield cursor.execute("SELECT * FROM user")
                except pymysql.err.ProgrammingError as e:
                    logging.error(e)
                    subprocess.Popen(['mysql',
                                    '--host={}'.format('127.0.0.1'),
                                    '--database={}'.format('chat'),
                                    '--user={}'.format('chat'),
                                    '--password={}'.format('chat')],
                                    stdin=open(self.settings['schema']))
                except:
                    yield conn.rollback()
                else:
                    yield conn.commit()
            yield self.pool.close()
    

    may_create_db函数中,我会尝试去连接数据库,如果对应的表不存在的话,那么就会创建相关的表,但现在运行的时候遇到了一点问题,就是那个pymysql.err.ProgrammingError 还是会显示出来。

    运行结果如下:

    ➜ /home/yundongx/gitroom/chat (dev) ✗ [chat]$ python app.py
    2016-10-04 21:27:10,677 INFO: Server starts on port 8888
    2016-10-04 21:27:10,704 ERROR: (1146, "Table 'chat.user' doesn't exist")
    mysql: [Warning] Using a password on the command line interface can be insecure.
    2016-10-04 21:27:10,710 ERROR: Future <tornado.concurrent.Future object at 0x7f39862ca2e8> exception was never retrieved: Traceback (most recent call last):
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/tornado/gen.py", line 1021, in run
        yielded = self.gen.throw(*exc_info)
      File "app.py", line 94, in may_create_db
        yield cursor.execute("SELECT * FROM test")
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/tornado/gen.py", line 1015, in run
        value = future.result()
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/tornado/concurrent.py", line 237, in result
        raise_exc_info(self._exc_info)
      File "<string>", line 3, in raise_exc_info
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/tormysql/util.py", line 14, in finish
        result = fun(*args, **kwargs)
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/pymysql/cursors.py", line 166, in execute
        result = self._query(query)
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/pymysql/cursors.py", line 322, in _query
        conn.query(q)
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/pymysql/connections.py", line 835, in query
        self._affected_rows = self._read_query_result(unbuffered=unbuffered)
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/pymysql/connections.py", line 1019, in _read_query_result
        result.read()
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/pymysql/connections.py", line 1302, in read
        first_packet = self.connection._read_packet()
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/pymysql/connections.py", line 981, in _read_packet
        packet.check_error()
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/pymysql/connections.py", line 393, in check_error
        err.raise_mysql_exception(self._data)
      File "/home/yundongx/.virtualenvs/chat/lib/python3.5/site-packages/pymysql/err.py", line 107, in raise_mysql_exception
        raise errorclass(errno, errval)
    pymysql.err.ProgrammingError: (1146, "Table 'chat.test' doesn't exist")
    

    我的疑问

    1. 我调用may_create_db 的方式是否正确?
    2. 我了解到那个异常显示的原因是因为相关Future的异常没有被取出,所以自动加到了logging里面,打印了出来,如何才能让那个异常不显示出来呢?
    12 条回复    2016-10-09 21:35:03 +08:00
    bwangel
        1
    bwangel  
    OP
       2016-10-04 21:47:41 +08:00
    @sujin190 , Please help me!
    bwangel
        2
    bwangel  
    OP
       2016-10-04 22:07:21 +08:00
    好吧,脑子犯迷糊了,调用方式铁定有问题啊!

    应该这么调用:

    ```
    def main():
    tornado.options.parse_command_line()
    app = Application()
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    logging.info("Server starts on port {}".format(options.port))
    ioloop = tornado.ioloop.IOLoop.current()
    ioloop.run_sync(app.may_create_db)
    ioloop.start()


    if __name__ == "__main__":
    main()
    ```
    bwangel
        3
    bwangel  
    OP
       2016-10-04 22:11:23 +08:00
    错误是在 _TracebackLogger 里面抛出来的。

    http://www.tornadoweb.org/en/stable/_modules/tornado/concurrent.html

    它的 docstring 里面就说了

    However, we don't want to log the exception as soon as
    set_exception() is called: if the calling code is written
    properly, it will get the exception and handle it properly. But
    we *do* want to log it if result() or exception() was never called

    它只会记录 result() 或者 exception() 没有被调用的异常,我就想估计是调用方式出错了,好吧,真的是。
    sujin190
        4
    sujin190  
       2016-10-06 21:28:10 +08:00
    @bwangel 放假回家了,没网,不好意思
    似乎不是这个问题啊,从日志上看你那个 try 是生效了,不应该再出现后面的错误才,回去后我调试下看看是否还有其他的问题吧
    bwangel
        5
    bwangel  
    OP
       2016-10-07 21:37:47 +08:00
    @sujin190 ,已经解决了。

    我感觉我那样的调用方式好像不太对,我个人理解是这样的:

    我是在 app 的构造函数里面直接调用 may_create_db 这个函数的,此时 ioloop 还没有生成,此时那个查询语句的跑出异常了,但是还不能进行相应触发(因为没有 ioloop ),也就意味着不能调用相应 Future 的 set_exception 函数。所以这个异常就会被放到_TracebackLogger 里面,重新输出一遍。


    还有一个问题想请教一下,请问一下如果我写了一个查询函数,类似于

    @gen.coroutine
    def select():
    利用 tormysql 做一些查询

    这个查询函数能够直接被调用吗?还是必须要放到 ioloop.run_sync 中调用,或者放到另外一个 coroutine 中通过 yield 调用。
    sujin190
        6
    sujin190  
       2016-10-07 21:43:51 +08:00
    @bwangel 但我觉得不是这个问题,能贴一下完整代码么? ioloop 是自动生成的, start 只是驱动其运行而已
    bwangel
        7
    bwangel  
    OP
       2016-10-07 21:52:39 +08:00
    bwangel
        8
    bwangel  
    OP
       2016-10-07 21:53:53 +08:00
    项目仓库就是这个 Chat ,写的一个很简单的 Demo 项目。
    bwangel
        9
    bwangel  
    OP
       2016-10-07 21:57:55 +08:00
    sujin190
        10
    sujin190  
       2016-10-08 12:54:40 +08:00
    @bwangel 我测试了下你给的代码,没有你说的这个问题啊。。
    bwangel
        11
    bwangel  
    OP
       2016-10-09 14:19:37 +08:00
    @sujin190 ,我又新建文件尝试重现这个错误,确实没有了。。好诡异啊!
    sujin190
        12
    sujin190  
       2016-10-09 21:35:03 +08:00
    @bwangel 。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5218 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 09:35 · PVG 17:35 · LAX 01:35 · JFK 04:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.