【问题标题】:Attribute Exception Error Raised When I Run This CLI app运行此 CLI 应用程序时引发属性异常错误
【发布时间】:2014-06-25 14:28:17
【问题描述】:

我在 Docopt 中编写了一个小型 CLI 待办事项应用程序,但是当我运行它时 python t.py 最后我得到了这个异常,但一切似乎都运行良好。当我将命令传递给应用程序时,我根本没有任何异常。还有一件事,如果我删除 __del__ 方法不会出现异常,但我认为我们需要关闭 sqlite db 连接。有什么建议吗?

Exception AttributeError: "'Todo' object has no attribute 'db'" in <bound method Todo.__del__ of <__main__.Todo object at 0x1038dac50>> ignored

应用代码:

"""t, a unix command-line todo application

Usage:
  t   add <task>
  t   check <id>
  t   uncheck <id>
  t   clear
  t   ls [--all]
  t   -h | --help
  t   --version

Commands:
  add           Add a new task
  check         Check a new task as done
  uncheck       Uncheck a task as done
  clear         Refresh the database
  ls            List all tasks

Options:
  -h --help     Show this screen.
  --version     Show version.
  --all         List all tasks
"""
import sqlite3
import os
import datetime
from docopt import docopt
from termcolor import colored
from prettytable import PrettyTable

SMILEY = "\xF0\x9F\x98\x83"  # Smiley emoji
GRIN = "\xF0\x9F\x98\x81"  # Grin face emoji


def echo(msg, err=False):
    """
    A simple function for printing to terminal with colors and emoji's
    """
    if err:
        print colored(msg + " " + GRIN, "red")
    else:
        print colored(msg + " " + SMILEY, "cyan")


class Todo(object):

    def __init__(self):
        """
        Set up the db and docopt upon creation of object
        """
        self.arg = docopt(__doc__, version=0.10)
        # Create a path to store the database file
        db_path = os.path.expanduser("~/")
        self.db_path = db_path + "/" + ".t-db"
        self.init_db()

    def init_db(self):
        self.db = sqlite3.connect(self.db_path)
        self.cursor = self.db.cursor()
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS todo(id INTEGER PRIMARY KEY, task TEXT,
            done INT, date_added TEXT, date_completed TEXT)
            ''')
        self.db.commit()

    def run(self):
        """
        Parse the arg's using docopt and route to the respoctive methods
        """
        if self.arg['add']:
            self.add_task()
        elif self.arg['check']:
            self.check_task()
        elif self.arg['uncheck']:
            self.uncheck_task()
        elif self.arg['clear']:
            self.clear_task()
        else:
            if self.arg['--all']:
                self.list_task()
            else:
                self.list_pending_tasks()

    def _record_exists(self, id):
        """
        Checks if the record exists in the db
        """
        self.cursor.execute('''
            SELECT * FROM todo WHERE id=?
          ''', (id,))
        record = self.cursor.fetchone()
        if record is None:
            return False
        return True

    def _is_done(self, id):
        """
        Checks if the task has already been marked as done
        """
        self.cursor.execute('''
            SELECT done FROM todo WHERE id=?
          ''', (id,))
        record = self.cursor.fetchone()
        if record == 0:
            return False
        return True

    def add_task(self):
        """
        Add a task todo to the db
        """
        task = self.arg['<task>']
        date = datetime.datetime.now()
        date_now = "%s-%s-%s" % (date.day, date.month, date.year)
        self.cursor.execute('''
          INSERT INTO todo(task, done, date_added)
          VALUES (?, ?, ?)
        ''', (str(task), 0, date_now))
        self.db.commit()
        echo("The task has been been added to the list")

    def check_task(self):
        """
        Mark a task as done
        """
        task_id = self.arg['<id>']
        date = datetime.datetime.now()
        date_now = "%s-%s-%s" % (date.day, date.month, date.year)
        if self._record_exists(task_id):
            self.cursor.execute('''
                UPDATE todo SET done=?, date_completed=? WHERE Id=?
            ''', (1, date_now, int(task_id)))
            echo("Task %s has been marked as done" % str(task_id))
            self.db.commit()
        else:
            echo("Task %s doesn't exist" % (str(task_id)), err=True)

    def uncheck_task(self):
        """
        Mark as done task as undone
        """
        task_id = self.arg['<id>']
        if self._record_exists(task_id):
            self.cursor.execute('''
                UPDATE todo SET done=? WHERE id=?
              ''', (0, int(task_id)))
            echo("Task %s has been unchecked" % str(task_id))
            self.db.commit()
        else:
            echo("Task %s doesn't exist" % str(task_id), err=True)

    def list_task(self):
        """
        Display all tasks in a table
        """
        tab = PrettyTable(["Id", "Task Todo", "Done ?", "Date Added",
                          "Date Completed"])
        tab.align["Id"] = "l"
        tab.padding_width = 1
        self.cursor.execute('''
            SELECT id, task, done, date_added, date_completed FROM todo
          ''')
        records = self.cursor.fetchall()
        for each_record in records:
            if each_record[2] == 0:
                done = "Nop"
            else:
                done = "Yup"
            if each_record[4] is None:
                status = "Pending..."
            else:
                status = each_record[4]
            tab.add_row([each_record[0], each_record[1], done,
                        each_record[3], status])
        print tab

    def list_pending_tasks(self):
        """
        Display all pending tasks in a tabular form
        """
        tab = PrettyTable(["Id", "Task Todo", "Date Added"])
        tab.align["Id"] = "l"
        tab.padding_width = 1
        self.cursor.execute('''
            SELECT id, task, date_added FROM todo WHERE done=?
          ''', (int(0),))
        records = self.cursor.fetchall()
        for each_record in records:
            tab.add_row([each_record[0], each_record[1], each_record[2]])
        print tab

    def clear_task(self):
        """
        Delete the table to refresh the app
        """
        self.cursor.execute('''
            DROP TABLE todo
          ''')
        self.db.commit()

    def __del__(self):
        self.db.close()


def main():
    """
    Entry point for console script
    """
    app = Todo()
    app.run()

if __name__ == "__main__":
    main()

【问题讨论】:

  • 也许你可以缩短你的程序,去掉任何与错误无关的细节。请参阅stackoverflow.com/help/mcve 了解有关什么是好的测试用例的信息。

标签: python


【解决方案1】:

我的调试会话告诉我,如果 docopt 无法解析给定的选项(在您的情况下,例如根本没有给出任何选项时),它会立即退出。

因此,在您的__init__ 中,在调用self.init_db() 以设置self.db 之前,调用docopt(),无法解析(未)给定选项并立即尝试执行exit(1) 之类的操作(我'我在这里猜),然后它又试图通过__del__-方法拆除Todo-object,但self.db成员变量还没有。

因此,“最佳”解决方法可能是在调用 docopt 之前设置数据库,或者告诉 docopt 也没有任何选项是可以的。

【讨论】:

  • 你能提供一个修复就好了
【解决方案2】:

避免使用__del__。如果您想确保一切都已关闭,我建议您在 run 方法中/之后显式调用 self.db.close() ,或者使用 atexit 模块注册关闭,另请参见 similar post

【讨论】:

    猜你喜欢
    • 2014-01-03
    • 2022-10-07
    • 1970-01-01
    • 2017-06-14
    • 1970-01-01
    • 2015-10-11
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    相关资源
    最近更新 更多