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 尝试打开同一文件时失败

有几种选择取决于您想要做什么:

  1. 使用 tempfile.mktemp() 获取临时文件的名称,请注意这是有原因的贬值
  2. 使用tempfile.mkdtemp() 获取临时目录的名称,您可以在其中创建数据库。这具有较少的安全问题,因此是首选
  3. 使用 tempfile.TemporaryDirectory() 将创建一个目录并在超出范围时自动将其删除。对于您的用例,这可能是 good/bad
  4. 通过 sqlite3.connect(':memory:') 在内存中创建数据库。再次取决于这是否真的是一个临时数据库,这可能是也可能不是你想要的

为了解释之前发生的事情,当你说它 "working" 时你实际上在做什么类似于:

import sqlite3, tempfile

a = tempfile.TemporaryFile()
b = a.name
del a
conn1 = sqlite3.connect(b)

这将:

  1. 创建一个 TemporaryFile 对象,它在磁盘上创建一个空的临时文件并为 reading/writing
  2. 打开它
  3. 获取刚刚创建的临时文件的名称
  4. 从作用域中删除 TemporaryFile 对象,这也会导致相关文件被删除
  5. 使用临时名称创建数据库

当您保留 TemporaryFile 时它失败的原因是该文件从未关闭和删除。 sqlite3 不喜欢这个,因此它失败了

这个解释太长了,希望它有道理!