Title: Python Access Denied using os.rename()
Tags: python

Question: I have been coding a little system that is sort of a supplies planner. It handles some files and, in case I need to cancel one purchase request, There are two rules: The folder containing the request's files cannot be deleted and it has to indicate in its name that it was in fact cancelled.
When I use os.rename() I get "PermissionError: [WinError 5] Acesso negado: 'c:/Users/jean.ribeiro/Downloads/Desenvolvimento/ERP Wannabe/2203 - UHE São Simão - BOP Mecânico - Documentos/07. Engenharia/07.05. Req de Compra/1.RCOs/2203-RCO-007 - Lista Preliminar de Tubos/' -> 'c:/Users/jean.ribeiro/Downloads/Desenvolvimento/ERP Wannabe/2203 - UHE São Simão - BOP Mecânico - Documentos/07. Engenharia/07.05. Req de Compra/1.RCOs/2203-RCO-007 - Lista Preliminar de Tubos - CANCELADA'" but my account does have permission to modify - it was my program that created the folder and some of the files in it in the first place.
What I am doing immediately before trying to rename the folder is to modify one excel file using openpyxl. I save it and delete the object right before renaming the folder it is located at.
How can I solve this error? To me it seems that maybe my program is still using the excel file while trying to rename its folder, but I don't see why, since I delete the object first. I'm sorry for some variables and text in portuguese, I can translate if it helps, just let me know.
caminho[0] is a string containing the full path to my folder and looks like
"c:/Users/myuser/some/folder/structure/target-folder"
I have only tried using relative path by using os.chdir() to the root folder containing my target folder before renaming it, but the same error occurred. Renaming manually (using windows explorer) works just fine.
Edit: the error message, translated:
PermissionError: [WinError 5] Access denied: 'c:/Users/jean.ribeiro/Downloads/Desenvolvimento/ERP Wannabe/2203 - UHE São Simão - BOP Mecânico - Documentos/07. Engenharia/07.05. Req de Compra/1.RCOs/2203-RCO-007 - Lista Preliminar de Tubos/' -> 'c:/Users/jean.ribeiro/Downloads/Desenvolvimento/ERP Wannabe/2203 - UHE São Simão - BOP Mecânico - Documentos/07. Engenharia/07.05. Req de Compra/1.RCOs/2203-RCO-007 - Lista Preliminar de Tubos - CANCELADA'
Edit 2:
The full function for easier understading. caminho is a tuple containing a full path the the target folder and the name of an excel file inside that folder. Note that the os.rename() statement works fine when run outside this function giving the same path caminho[0] contains
def editando(caminho, titulo):
    caminho_suprimentos = diretorio_atual + getPasta(int(caminho[1][:4])) + rcosup + caminho[0][:-1].split("/")[-1] + "/"
    sg.theme("SystemDefault")
    layout = [[[sg.Button("Revisar"), sg.Button("Cancelar")], [sg.Button("Voltar")]]]
    janela = sg.Window(f"Editando requisição {titulo}", layout, finalize=True, size=(300, 100), modal=True)
    janela.TKroot.focus_force()
    propagate = False

    while True:
        event, values = janela.read()
        if event == sg.WIN_CLOSED or event == "Voltar":
            break
        if event == "Revisar":
            if "Superado" not in os.listdir(caminho[0]): # if there is not an outdated folder...
                os.mkdir(caminho[0] + "Superado") # ... create it
                shutil.copy(caminho[0] + caminho[1], caminho[0] + "Superado/" + caminho[1][:-5] + " (R00).xlsx") # and put the outdated file there, renaming at destination
            else:
                rev = []
                for arq in os.listdir(caminho[0] + "Superado"):
                    if caminho[1][:12] in arq:
                        rev.append(int(arq[-8:-6]))
                rev = sorted(rev)
                rev = rev[-1]
                shutil.copy(caminho[0] + caminho[1], caminho[0] + "Superado/" + caminho[1][:-5] + f" (R{str(rev + 1).zfill(2)})" + ".xlsx")
            if "Superado" not in os.listdir(caminho_suprimentos):
                os.mkdir(caminho_suprimentos + "Superado")
                shutil.copy(caminho_suprimentos + caminho[1], caminho_suprimentos + "Superado/" + caminho[1][:-5] + " (R00).xlsx")
            else:
                shutil.copy(caminho_suprimentos + caminho[1], caminho_suprimentos + "Superado/" + caminho[1][:-5] + f" (R{str(rev + 1).zfill(2)})" + ".xlsx")
            pasta = openpyxl.load_workbook(caminho[0] + caminho[1])
            aba = pasta["Formulário"]
            try:
                emitente = os.getenv("username").split(".") # take the current username by looking into the system
                emitente = emitente[0].capitalize() + " " + emitente[1].capitalize()
            except:
                emitente = "Usuário não identificado"
            creation = datetime.datetime.now()
            aba["E41"].value = creation # set creation date
            aba["A41"].value = emitente # set creator
            pasta.save(caminho[0] + caminho[1])
            del pasta
            os.startfile(caminho[0] + caminho[1])
            propagate = True
        if event == "Cancelar":
            if sg.popup_ok_cancel("Você tem certeza? Esta ação não poderá ser desfeita.", font=("Helvetica", 12, "bold"), background_color="yellow", title="Confirme") == "OK":
                ind = cycleThrough(caminho[0] + caminho[1], dic=True) # ind[0] contains data, ind[1] contains metadata (=data_starts_at + data_ends_at) in cycleThrough
                pasta = openpyxl.load_workbook(caminho[0] + caminho[1])
                if "Anexo" in pasta.sheetnames:
                    aba = pasta["Anexo"]
                    for linha in aba.iter_rows(min_col=ind[1]["Quantidade"][0], max_col=ind[1]["Quantidade"][0], min_row=ind[1]["Quantidade"][1], max_row=ind[1]["Quantidade"][1] + ind[1]["data_ends_at"] - 1):
                        for celula in linha:
                            celula.value = 0
                elif "Formulário" in pasta.sheetnames:
                    aba = pasta["Formulário"]
                    for linha in aba.iter_rows(min_col=2, max_col=2, min_row=15, max_row=ind[1]["data_ends_at"] + 1):
                        for celula in linha:
                            celula.value = 0
                else:
                    sg.popup("A requisição sendo editada não segue o padrão. Zere as quantidades manualmente.")
                pasta.save(caminho[0] + caminho[1])
                pasta.save(caminho_suprimentos + caminho[1])
                del pasta
                os.rename(caminho[0], caminho[0][:-1] + " - CANCELADA/")
                os.rename(caminho_suprimentos, caminho_suprimentos[:-1] + " - CANCELADA/")
                sg.popup("Requisição Cancelada")
                break
    
    if propagate:
        shutil.copy(caminho[0] + caminho[1], caminho_suprimentos + caminho[1])
    janela.close()
    return True

Answer: As hinted by @Axe319 (thanks mate), something was keeping the file open, but it was not inside editando() nor was it a program I had open. Openpyxl apparently closes the file just fine when you del the object but for some reason it doesn't if you had opened it in read-only mode, which was being done in cycleThrough()
Adding wb._archive.close() after using the file inside cycleThrough() solved the issue. Thanks everyone for the help!