3

I am running Python 3.13.9 and have a script that creates a temp file, reads data from a database and outputs it as CSV in to said temp file and then sends that file to a server. I am trying to then delete the temp file when it is done.

    logging.info(f"Updating list {list["name"]} using session token {token}.")
    
    # Create a temporary CSV file
    with tempfile.NamedTemporaryFile(mode="w+", newline="", suffix=".csv", delete=False) as temp_file:
        temp_path = temp_file.name
        
        logging.info(f"Using temp file {temp_file.name}.")

        # Connect to SQL Server
        conn_str = (
            f"DRIVER={{{DRIVER}}};"
            f"SERVER={list["server"]};"
            f"DATABASE={list["database"]};"
            "Trusted_Connection=yes;"
            "TrustServerCertificate=yes;"
        )

        logging.info(f"Connecting to SQL Server {conn_str}.")
        with pyodbc.connect(conn_str) as conn:
            cursor = conn.cursor()

            logging.info(f"Executing query '{list["query"]}'.")
            cursor.execute(list["query"])

            logging.info(f"Writing column headers.")
            column_names = [column[0] for column in cursor.description]
            writer = csv.writer(temp_file)
            writer.writerow(column_names)

            logging.info(f"Writing data lines.")
            for row in cursor:
                writer.writerow(row)

        payload={} 
        files=[('file',(temp_file.name,open(temp_file.name,'rb'),'text/csv'))] 
        headers = {'X-Gatekeeper-SessionToken': token,'FastField-API-Key': APIKEY}
        logging.info(f"Sending list csv file to {list["url"]} using token {token}.")
        response = requests.request("PUT", list["url"], headers=headers, data=payload, files=files)
        logging.info(f"Received response {response.text}.")

        return response.text

    # Remove the temp file created above
    os.remove(temp_path)

This always gives me the error "PermissionError: [WinError 32] The process cannot access the file because it is being used by another process" at the os.remove line. I have tried manually closing the file first and get the same error. I have tried setting delete=True but then I get a permission error. How do I delete this file once I'm done with it?

2
  • 3
    The return suggests this is inside a function, but it isn't. Is the code incomplete? Commented 2 hours ago
  • The call to os.remove() will not be executed in the code as shown due to the preceding return. Also, it looks like you may have a variable (a dictionary) named list which seems a bit odd Commented 2 hours ago

3 Answers 3

3

The call tempfile.NamedTemporaryFile returns a wrapper around a file. In your case temp_file, which has been returned with the call, has attributes name (the file name) and file (the underlying system file). You can use either temp_file or temp_file.file for I/O operations. But nowhere in you code have you issued a call to close on the file. So when you go to remove the file you get the error you get.

You should either code an explicit temp_file.close() (or temp_file.file.close()) or use a context manager:

with tempfile.NamedTemporaryFile(mode="w+", newline="", suffix=".csv", delete=False) as temp_file:
    with temp_file.file as file:
        ...
    # The file will be closed when we reach here

Also, when you use tempfile.NamedTemporaryFile, the file is already opened for you so you should not issue an explicit open.

Sign up to request clarification or add additional context in comments.

1 Comment

2

Windows won’t let you delete a file while any handle has it open, and in your code you open the temp twice (the NamedTemporaryFile plus open(...,'rb')) so the upload handle stays open and Windows blocks os.remove. (Source)

Fix: close the file you send (use a with open(..., "rb") as f: for the upload) so the handle is closed before os.remove(); also on Windows prefer delete=False and explicitly close the temp before reopening. Example:

with tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False) as tmp:
    tmp.write(csv_text)

with open(tmp.name, "rb") as f:
    files = [('file', (os.path.basename(tmp.name), f, 'text/csv'))]
    resp = requests.put(url, headers=headers, files=files)

os.remove(tmp.name)

Python’s tempfile docs explain the Windows behavior (you must close or use delete=False), and Microsoft’s docs explain you cannot delete files that are open by a process — that’s why this happens and why Windows’ file-locking makes this workflow fiddly. (Source)

1 Comment

You are implying in your answer that the file stays open because it is being opened twice. But that is not correct. The second open is not doing anything and the file is open because no close was ever issued against it.
0

Collect all your data and close the file handle by exiting the with block.

Open the file for reading within the requests call using a second with block (to make sure it closes immediately after sending).

Place the os.remove call inside a try...finally block to ensure it happens even if the upload fails, and ensure it is before the return statement.

Corrected Code:

import os
import csv
import pyodbc
import logging
import requests
import tempfile

def update_list(list_info, token, APIKEY):
    logging.info(f"Updating list {list_info['name']} using session token {token}.")
    
    # 1. Create and Write to the file
    # We use delete=False because Windows handles file locks strictly
    temp_path = None
    try:
        with tempfile.NamedTemporaryFile(mode="w+", newline="", suffix=".csv", delete=False) as temp_file:
            temp_path = temp_file.name
            logging.info(f"Using temp file {temp_path}.")

            conn_str = (
                f"DRIVER={{{DRIVER}}};"
                f"SERVER={list_info['server']};"
                f"DATABASE={list_info['database']};"
                "Trusted_Connection=yes;"
                "TrustServerCertificate=yes;"
            )

            with pyodbc.connect(conn_str) as conn:
                cursor = conn.cursor()
                cursor.execute(list_info["query"])
                
                writer = csv.writer(temp_file)
                writer.writerow([column[0] for column in cursor.description]) # Headers
                for row in cursor:
                    writer.writerow(row)
        
        # At this point, the 'with' block has ended. 
        # The file is SAVED and CLOSED. Windows will now allow other operations.

        # 2. Upload the file
        headers = {'X-Gatekeeper-SessionToken': token, 'FastField-API-Key': APIKEY}
        
        # We open the file in read-binary mode inside another 'with' 
        # so it closes immediately after the request is finished
        with open(temp_path, 'rb') as f:
            files = [('file', (os.path.basename(temp_path), f, 'text/csv'))]
            logging.info(f"Sending list csv file to {list_info['url']}.")
            response = requests.put(list_info["url"], headers=headers, files=files)
        
        logging.info(f"Received response {response.text}.")
        result_text = response.text

    finally:
        # 3. Cleanup: This runs regardless of success or errors above
        if temp_path and os.path.exists(temp_path):
            try:
                os.remove(temp_path)
                logging.info(f"Successfully deleted temp file {temp_path}.")
            except Exception as e:
                logging.error(f"Failed to delete temp file: {e}")

    # 4. Return the result AFTER cleanup
    return result_text
New contributor
MintForge π is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

2 Comments

Surely the test os.path.exists(temp_path) is superfluous
The file has already been opened with the call to tempfile.NamedTemporaryFile so trying to open it a second call with your explicit call to open makes no sense. If you want the file to be automatically closed using a context manager, then just code with temp_file.file:.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.