165

Answers to Best way to generate random file names in Python show how to create temporary files in Python.

I only need to have a temporary file name in my case.
Calling tempfile.NamedTemporaryFile() returns a file handle after actual file creation.

Is there a way to get a filename only? I tried this:

# Trying to get temp file path
tf = tempfile.NamedTemporaryFile()
temp_file_name = tf.name
tf.close()
# Here is my real purpose to get the temp_file_name
f = gzip.open(temp_file_name ,'wb')
...
Davide Fiocco
  • 5,350
  • 5
  • 35
  • 72
Hill
  • 3,391
  • 3
  • 24
  • 28
  • 7
    `NamedTemporaryFile` guarantees a unique name, (probably) by trying it and retrying if it exists. Getting just a name won't guarantee that you can actually create the file later, you're opening to the race condition of someone else using the same name before you. – Joachim Isaksson Oct 24 '14 at 04:24
  • 7
    @Joachim True, there is a race condition here and it would be preferred to avoid that. However, sometimes you have to pass a temporary file name to a function (file open happening internally.) Having a nicely random name gives a much better probability that the race condition will be a non-issue. I think there is a valid need to provide a good temporary filename to minimize the chance of a race condition failure. Of course adding a good prefix and suffix based on the running process and task being performed will provide even less chance of a collision. – PolyMesh Apr 06 '15 at 18:47
  • @PolyMesh You can avoid the race condition by creating a temporary directory then using a fixed name file within it. So your function accepts a directory, rather than a file, and always creates the same file. – DylanYoung Dec 07 '18 at 19:08
  • use tarfile and pass it the fileobj – Wyrmwood May 08 '19 at 18:58
  • 1
    @DylanYoung: While clever, isolating work to a temporary directory *still* exposes your code to races. Even if you explicitly change permissions on that directory to exclude competing users, you can *never* exclude superusers or competing processes of the same user – and, of course, you'd only be introducing even more subtle races (e.g., if a competing process creates the same temporary directory and changes that directory's permissions before you get the chance). It's probably saner just to obfuscate the temporary filename and tightly clutch your GNU manual with a white-knuckle grip. – Cecil Curry Oct 21 '21 at 06:21
  • @CecilCurry Yeah I don't know what I was thinking there. It's the same race condition, lol. Though I'm surprised python or the OS doesn't guard against it... Using a unique prefix or suffix is definitely a better way to go, assuming you have a unique identifier to use. – DylanYoung Oct 21 '21 at 20:20
  • Doesn't tempfile.TemporaryDirectory only return when it successfully creates a new directory? As @JoachimIsaksson noted, perhaps trying and retrying with random names, but still, if all processes follow this protocol, then the race condition should be avoided, right? –  Nov 24 '22 at 12:14

9 Answers9

105

I think the easiest, most secure way of doing this is something like:

path = os.path.join(tempfile.mkdtemp(), 'something')

A temporary directory is created that only you can access, so there should be no security issues, but there will be no files created in it, so you can just pick any filename you want to create in that directory. Remember that you do still have to delete the folder after.

edit: In Python 3 you can now use tempfile.TemporaryDirectory() as a context manager to handle deletion for you:

with tempfile.TemporaryDirectory() as tmp:
  path = os.path.join(tmp, 'something')
  # use path
TheTechRobo the Nerd
  • 1,249
  • 15
  • 28
alecbz
  • 6,292
  • 4
  • 30
  • 50
  • 1
    As Daniel Braun mentioned above: It is important to remember when using `tempfile.mkdtemp` the user is responsible for deleting the temporary directory and its contents when done with it. – bitinerant Oct 02 '19 at 20:11
  • 16
    If you use `tempfile.TemporaryDirectory()` as a context manager, it will be deleted for you. – gerrit Mar 26 '20 at 13:00
  • 2
    I really like this because it handles deletion automatically. In my case, it was useful to make a temporary filename for shutil.make_archive function because it requires the archive's basename as input, not a file-like object. – Kota Mori Aug 27 '21 at 03:17
96

If you want a temp file name only you can call inner tempfile function _get_candidate_names():

import tempfile

temp_name = next(tempfile._get_candidate_names())
% e.g. px9cp65s

Calling next again, will return another name, etc. This does not give you the path to temp folder. To get default 'tmp' directory, use:

defult_tmp_dir = tempfile._get_default_tempdir()
% results in: /tmp 
radtek
  • 34,210
  • 11
  • 144
  • 111
Marcin
  • 215,873
  • 14
  • 235
  • 294
  • 3
    better way to create a temp directory is `temp_dir = tempfile.mkdtemp(prefix='some-prefix_')` which will safely create a temporary directory and return a string with the absolute path. – Emanuel Ey Sep 25 '15 at 13:59
  • 6
    It's important to point out that `next(tempfile._get_candidate_names())` does not necessarily return a non-existant path, that's why user-level tempfile interfaces can [try several names until an unused one is found](https://github.com/python/cpython/blob/cafaf0447b950fd4f59edd8cbde040c61ae528f8/Lib/tempfile.py#L255): – Eli Korvigo Jul 14 '18 at 16:42
  • 4
    One could use public `tempfile.gettempdir()` instead of private `tempfile._get_default_tempdir()`. – flonk Aug 07 '18 at 12:38
  • @EmanuelEy It is important to remember when using `tempfile.mkdtemp` the user is responsible for deleting the temporary directory and its contents when done with it. – Daniel Braun Dec 14 '18 at 07:28
12

tempfile.mktemp() do this.

But note that it's deprecated. However it will not create the file and it is a public function in tempfile compared to using the _get_candidate_names().

The reason it's deprecated is due to the time gap between calling this and actually trying to create the file. However in my case the chance of that is so slim and even if it would fail that would be acceptable. But it's up to you to evaluate for your usecase.

Zitrax
  • 19,036
  • 20
  • 88
  • 110
  • 1
    “even if it would fail that would be acceptable”; the race condition is not merely a risk of failure, it is a security risk (see the `tempfile.mktemp` documentation). So that should not be considered acceptable. – bignose Feb 14 '16 at 21:42
  • 8
    @bignose It's a *potential* security issue. It depends on what you want to do, the execution environment you're in, etc. That said: it may be more secure to do something like `os.path.join(tempfile.mkdtemp(), 'something')` There at least the directory is created (and owned by you, I presume). – alecbz Aug 21 '17 at 17:49
10

It may be a little late, but is there anything wrong with this?

import tempfile
with tempfile.NamedTemporaryFile(dir='/tmp', delete=False) as tmpfile:
    temp_file_name = tmpfile.name
f = gzip.open(temp_file_name ,'wb')
russell
  • 660
  • 1
  • 10
  • 18
6

Combining the previous answers, my solution is:

def get_tempfile_name(some_id):
    return os.path.join(tempfile.gettempdir(), next(tempfile._get_candidate_names()) + "_" + some_id)

Make some_id optional if not needed for you.

juanmirocks
  • 4,786
  • 5
  • 46
  • 46
  • Again, the candidate names might not be actually available. This is the correct answer: https://stackoverflow.com/a/45803022/6387880 – j4hangir May 09 '20 at 00:04
  • 1
    However, likely one needs to create random names. Nonetheless, to be sure, if `_get_candidate_names()` does not exist, one can default to some semi-random string generator. For example some uuid. – juanmirocks May 12 '20 at 11:41
6

Let's try not to overthink it:

import os, uuid, tempfile as tf

def make_temp_name(dir = tf.gettempdir()):
    return os.path.join(dir, str(uuid.uuid1()))
Adam Sosnowski
  • 1,126
  • 9
  • 7
5

As Joachim Isaksson said in the comments, if you just get a name you may have problems if some other program happens to use that name before your program does. The chances are slim, but not impossible.

So the safe thing to do in this situation is to use the full GzipFile() constructor, which has the signature

GzipFile( [filename[, mode[, compresslevel[, fileobj]]]])

So you can pass it the open fileobj, and a filename as well, if you like. See the gzip docs for details.

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
5

I would do it this way:

import tempfile
import os.path
import random
import string

def generate_temp_filename() -> str:
    random_string = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
    return os.path.join(tempfile.gettempdir(), random_string)

Advantage over other answers:

  • Does not rely on private functions (functions starting with _)
  • Does not create a file or directory
  • Does not use deprecated functions
user42723
  • 467
  • 3
  • 8
  • **...not bad, young Padawan.** You're still racing competing processes, but at least you're no longer demolishing your own codebase with deprecated and/or private stdlib utility functions. If directory creation isn't a compelling concern, I'd give [the `tempfile.TemporaryDirectory()` approach](https://stackoverflow.com/a/45803022/2809027) a slight nudge over this one. It's a matter of pick-your-poison at this point, really. – Cecil Curry Oct 21 '21 at 06:29
  • Deserves more upvotes. This is what I went for. – Contango Jan 24 '22 at 16:38
0
from random import sample
from string import digits, ascii_letters
from tempfile import gettempdir
from pathlib import Path

filename = Path(gettempdir()) / ''.join(sample(ascii_letters + digits, 10))
## PosixPath('/tmp/fHyMSeVsY8') or
## 
filename = Path(gettempdir()).joinpath(''.join(sample(ascii_letters + digits, 10))).as_posix()
## '/tmp/fHyMSeVsY8'

f = gzip.open(filename ,'wb')
britodfbr
  • 1,747
  • 14
  • 16