tempfile.TemporaryFile() 在分配给变量后不适用于 sqlite3
tempfile.TemporaryFile() does not work with sqlite3 after being assigned to a variable
我在 Python.
中使用临时文件和 sqlite3 模块
以下代码有效:
import sqlite3, tempfile
conn1 = sqlite3.connect(tempfile.TemporaryFile().name)
因此,我希望以下代码也能正常工作,但事实并非如此:
import sqlite3, tempfile
database_file = tempfile.TemporaryFile()
conn2 = sqlite3.connect(database_file.name)
sqlite3.OperationalError: unable to open database file
我能够使用 this answer 提取 conn1 使用的文件路径。将其输入 sqlite.connect 也同样有效:
import sqlite3, tempfile
conn1 = sqlite3.connect(tempfile.TemporaryFile().name)
cur = conn1.cursor()
cur.execute("PRAGMA database_list")
row = cur.fetchone()
database_file_path = row[2]
conn3 = sqlite3.connect(database_file_path)
似乎我可以使用 TemporaryFile().name,只要我不将它保存在变量中即可。这是有问题的,因为在我的真实代码中我需要存储临时文件的路径。我可以通过使用用于生成 conn3 的代码来解决所有这些问题,但是创建额外的数据库连接和 SQL 查询似乎没有任何理由是非常丑陋和低效的。
您应该使用 NamedTemporaryFile
而不是 TemporaryFile
。大概你 运行 在 MS Windows 下,它不支持未命名的文件,所以这两个是相同的,因此你没有注意到任何区别
就是说,您根本不应该使用 *TemporaryFile
,因为它们会创建并打开文件以保护您免受并发进程的干扰。该文件可能已被锁定,因此当 sqlite 尝试打开同一文件时失败
有几种选择取决于您想要做什么:
- 使用
tempfile.mktemp()
获取临时文件的名称,请注意这是有原因的贬值
- 使用
tempfile.mkdtemp()
获取临时目录的名称,您可以在其中创建数据库。这具有较少的安全问题,因此是首选
- 使用
tempfile.TemporaryDirectory()
将创建一个目录并在超出范围时自动将其删除。对于您的用例,这可能是 good/bad
- 通过
sqlite3.connect(':memory:')
在内存中创建数据库。再次取决于这是否真的是一个临时数据库,这可能是也可能不是你想要的
为了解释之前发生的事情,当你说它 "working" 时你实际上在做什么类似于:
import sqlite3, tempfile
a = tempfile.TemporaryFile()
b = a.name
del a
conn1 = sqlite3.connect(b)
这将:
- 创建一个
TemporaryFile
对象,它在磁盘上创建一个空的临时文件并为 reading/writing 打开它
- 获取刚刚创建的临时文件的名称
- 从作用域中删除
TemporaryFile
对象,这也会导致相关文件被删除
- 使用临时名称创建数据库
当您保留 TemporaryFile
时它失败的原因是该文件从未关闭和删除。 sqlite3 不喜欢这个,因此它失败了
这个解释太长了,希望它有道理!
我在 Python.
中使用临时文件和 sqlite3 模块以下代码有效:
import sqlite3, tempfile
conn1 = sqlite3.connect(tempfile.TemporaryFile().name)
因此,我希望以下代码也能正常工作,但事实并非如此:
import sqlite3, tempfile
database_file = tempfile.TemporaryFile()
conn2 = sqlite3.connect(database_file.name)
sqlite3.OperationalError: unable to open database file
我能够使用 this answer 提取 conn1 使用的文件路径。将其输入 sqlite.connect 也同样有效:
import sqlite3, tempfile
conn1 = sqlite3.connect(tempfile.TemporaryFile().name)
cur = conn1.cursor()
cur.execute("PRAGMA database_list")
row = cur.fetchone()
database_file_path = row[2]
conn3 = sqlite3.connect(database_file_path)
似乎我可以使用 TemporaryFile().name,只要我不将它保存在变量中即可。这是有问题的,因为在我的真实代码中我需要存储临时文件的路径。我可以通过使用用于生成 conn3 的代码来解决所有这些问题,但是创建额外的数据库连接和 SQL 查询似乎没有任何理由是非常丑陋和低效的。
您应该使用 NamedTemporaryFile
而不是 TemporaryFile
。大概你 运行 在 MS Windows 下,它不支持未命名的文件,所以这两个是相同的,因此你没有注意到任何区别
就是说,您根本不应该使用 *TemporaryFile
,因为它们会创建并打开文件以保护您免受并发进程的干扰。该文件可能已被锁定,因此当 sqlite 尝试打开同一文件时失败
有几种选择取决于您想要做什么:
- 使用
tempfile.mktemp()
获取临时文件的名称,请注意这是有原因的贬值 - 使用
tempfile.mkdtemp()
获取临时目录的名称,您可以在其中创建数据库。这具有较少的安全问题,因此是首选 - 使用
tempfile.TemporaryDirectory()
将创建一个目录并在超出范围时自动将其删除。对于您的用例,这可能是 good/bad - 通过
sqlite3.connect(':memory:')
在内存中创建数据库。再次取决于这是否真的是一个临时数据库,这可能是也可能不是你想要的
为了解释之前发生的事情,当你说它 "working" 时你实际上在做什么类似于:
import sqlite3, tempfile
a = tempfile.TemporaryFile()
b = a.name
del a
conn1 = sqlite3.connect(b)
这将:
- 创建一个
TemporaryFile
对象,它在磁盘上创建一个空的临时文件并为 reading/writing 打开它
- 获取刚刚创建的临时文件的名称
- 从作用域中删除
TemporaryFile
对象,这也会导致相关文件被删除 - 使用临时名称创建数据库
当您保留 TemporaryFile
时它失败的原因是该文件从未关闭和删除。 sqlite3 不喜欢这个,因此它失败了
这个解释太长了,希望它有道理!