tempfile.TemporaryFile() 在分配给变量后不适用于 sqlite3 [英] tempfile.TemporaryFile() does not work with sqlite3 after being assigned to a variable

查看:30
本文介绍了tempfile.TemporaryFile() 在分配给变量后不适用于 sqlite3的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Python 中使用 tempfile 和 sqlite3 模块.

以下代码有效:

导入 sqlite3,临时文件conn1 = sqlite3.connect(tempfile.TemporaryFile().name)

因此,我希望以下代码也能正常工作,但事实并非如此:

导入 sqlite3,临时文件database_file = tempfile.TemporaryFile()conn2 = sqlite3.connect(database_file.name)

<块引用>

sqlite3.OperationalError: 无法打开数据库文件

我能够使用这个答案来提取 conn1 使用的文件路径.将其输入 sqlite.connect 也能正常工作:

导入 sqlite3,临时文件conn1 = sqlite3.connect(tempfile.TemporaryFile().name)cur = conn1.cursor()cur.execute("PRAGMA database_list")行 = cur.fetchone()database_file_path = 行[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() 它将创建一个目录并在超出范围时自动删除它.这可能对您的用例有利/不利
  4. 通过执行 sqlite3.connect(':memory:') 在内存中创建数据库.再次取决于这是否真的是一个临时数据库,这可能是您想要的,也可能不是您想要的

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

导入 sqlite3,临时文件a = tempfile.TemporaryFile()b = a.name德尔阿conn1 = sqlite3.connect(b)

这将:

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

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

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

I am using the tempfile and sqlite3 modules in Python.

The following code works:

import sqlite3, tempfile
conn1 = sqlite3.connect(tempfile.TemporaryFile().name)

Thus, I would expect the following code to work as well, but it doesn't:

import sqlite3, tempfile
database_file = tempfile.TemporaryFile()
conn2 = sqlite3.connect(database_file.name)

sqlite3.OperationalError: unable to open database file

I was able to use this answer to extract the file path used by conn1. Feeding that into sqlite.connect worked as well:

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)

It seems that I can use TemporaryFile().name, so long as I don't save it in a variable. This is problematic, because in my real code I need to store the path to the temporary file. I could get around all this by using the code used to generate conn3, but it's horribly ugly and inefficient to create an extra database connection and SQL query for seemingly no reason.

解决方案

you should be using NamedTemporaryFile instead of TemporaryFile. presumably you're running under MS Windows which doesn't support unnamed files so these two are the same and hence you don't notice any difference

that said, you shouldn't be using *TemporaryFile at all, as these create and open the file in order protect you from concurrent processes messing you up. the file is presumably locked and hence when sqlite tries to open same file it fails

there are a few alternatives depending on what you want to do:

  1. use tempfile.mktemp() to get the name of a temporary file, note this is depreciated for a reason
  2. use tempfile.mkdtemp() to get the name of a temporary directory, into which you can create the database. this has less security issues so is preferred
  3. use tempfile.TemporaryDirectory() which will create a directory and automatically delete it when it goes out of scope. this might be good/bad for your use case
  4. create the database in memory by doing sqlite3.connect(':memory:'). again depending on whether this really is a temporary database this may or may not be what you want

to explain what was going on before, what you were actually doing when you said it's "working" is similar to:

import sqlite3, tempfile

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

this would:

  1. create a TemporaryFile object, which creates an empty temporary file on the disk and opens it for reading/writing
  2. get the name of the temporary file that was just created
  3. remove the TemporaryFile object from scope, which would also cause the associated file to be removed
  4. use the temporary name to create the database

the reason it failed when when you kept the TemporaryFile around was that the file was never closed and removed. sqlite3 didn't like this and hence it failed

that explanation got far too long, hope it makes sense!

这篇关于tempfile.TemporaryFile() 在分配给变量后不适用于 sqlite3的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆