[Bug]: Rename PDF Sheets not working
✈ Pre-Flight checks
- [X] I don't have SentinelOne antivirus installed (see above for the solution)
- [X] I have searched in the issues (open and closed) but couldn't find a similar issue
- [X] I have searched in the pyRevit Forum for similar issues
- [X] I already followed the installation troubleshooting guide thoroughly
- [X] I am using the latest pyRevit Version
🐞 Describe the bug
Rename PDF Sheets does not work
⌨ Error/Debug Message
IronPython Traceback:
Traceback (most recent call last):
File "C:\Program Files\pyRevit-Master\extensions\pyRevitTools.extension\pyRevit.tab\Drawing Set.panel\Sheets.pulldown\Rename PDF Sheets.pushbutton\script.py", line 35, in <module>
File "C:\Program Files\pyRevit-Master\extensions\pyRevitTools.extension\pyRevit.tab\Drawing Set.panel\Sheets.pulldown\Rename PDF Sheets.pushbutton\script.py", line 20, in renamePDF
IndexError: index out of range: 0
Script Executor Traceback:
System.IndexOutOfRangeException: index out of range: 0
at IronPython.Runtime.Operations.PythonOps.FixIndex(Int32 v, Int32 len)
at IronPython.Runtime.List.get_Item(Int32 index)
at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
at Microsoft.Scripting.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
at Microsoft.Scripting.Interpreter.LightLambda.Run2[T0,T1,TRet](T0 arg0, T1 arg1)
at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2)
at Microsoft.Scripting.Interpreter.DynamicInstruction`4.Run(InterpretedFrame frame)
at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
at Microsoft.Scripting.Interpreter.LightLambda.Run2[T0,T1,TRet](T0 arg0, T1 arg1)
at IronPython.Compiler.PythonScriptCode.RunWorker(CodeContext ctx)
at PyRevitLabs.PyRevit.Runtime.IronPythonEngine.Execute(ScriptRuntime& runtime)
♻️ To Reproduce
- Go to "Rename PDF Sheets"
- Browse to folder
- Crash occurs after hitting ok
⏲️ Expected behavior
Rename PDF tool to work
🖥️ Hardware and Software Setup (please complete the following information)
==> Registered Clones (full git repos)
==> Registered Clones (deployed from archive/image)
master | Deploy: "basepublic" | Branch: "master" | Version: "4.8.16.24121+2117" | Path: "C:\Users\ptomfohrde\AppData\Roaming\pyRevit-Master"
==> Attachments
Unnamed-363FBF8 | Product: "Autodesk Revit 2024" | Engine: IPY277 (277) | Path: "C:\Program Files\pyRevit-Master" | AllUsers
Unnamed-363FBF8 | Product: "Autodesk Revit 2023" | Engine: IPY277 (277) | Path: "C:\Program Files\pyRevit-Master" | AllUsers
Unnamed-363FBF8 | Product: "2022.1" | Engine: IPY277 (277) | Path: "C:\Program Files\pyRevit-Master" | AllUsers
Unnamed-363FBF8 | Product: "Autodesk Revit 2021" | Engine: IPY277 (277) | Path: "C:\Program Files\pyRevit-Master" | AllUsers
Unnamed-363FBF8 | Product: "2020.2.0" | Engine: IPY277 (277) | Path: "C:\Program Files\pyRevit-Master" | AllUsers
==> Installed Extensions
==> Default Extension Search Path
C:\Users\ptomfohrde\AppData\Roaming\pyRevit\Extensions
==> Extension Search Paths
==> Extension Sources - Default
https://github.com/eirannejad/pyRevit/raw/master/extensions/extensions.json
==> Extension Sources - Additional
==> Installed Revits
Autodesk Revit 2024 | Version: 24.2.0.63 | Build: 20231029_1515(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2024\"
Autodesk Revit 2023 | Version: 23.1.20.70 | Build: 20230510_1100(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2023\"
2022.1 | Version: 22.1.1.516 | Build: 20210921_1515(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2022\"
Autodesk Revit 2021 | Version: 21.1.21.45 | Build: 20201116_1100(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2021\"
2020.2.0 | Version: 20.2.90.12 | Build: 20220517_1515(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2020\"
==> Running Revit Instances
Error: Object reference not set to an instance of an object.
Run with "--debug" option to see debug messages
Additional context
Running Windows 11 Pro on Lenovo Thinkpad , Revit 2024
Hi @countdookuw, sorry for being so late in replying!
Do you by any chance still have the list of PDF file names?
The script expects that the file is named like <zero or more chars>Sheet - <name to use>.
The error you see is because one of the file in the chosen directory has "Sheet" in it, so it passes the first test
https://github.com/pyrevitlabs/pyRevit/blob/f9fa0d296386d548e198324dd752fb7fb5cd7b58/extensions/pyRevitTools.extension/pyRevit.tab/Drawing%20Set.panel/Sheets.pulldown/Rename%20PDF%20Sheets.pushbutton/script.py#L33
but it doesn't contain "Sheet - " (with space, hyphen and space after the word) as checked with the regular expression here:
https://github.com/pyrevitlabs/pyRevit/blob/f9fa0d296386d548e198324dd752fb7fb5cd7b58/extensions/pyRevitTools.extension/pyRevit.tab/Drawing%20Set.panel/Sheets.pulldown/Rename%20PDF%20Sheets.pushbutton/script.py#L19
This bug can be resolved in 2 ways: checking the presence of the entire "Sheet - " text from the start, or use a better code for the renamePDF function: it uses the findall method to then pick the first result, so it might be better use match and then check if the returning value is not None
Hello everyone ! it's my first comment and futur commit to Pyrevit !
i have tried différent méthode to solve this issus and test run time, it's not so much but i thing it's faster without regex :
if it's ok i will also add a subfolder option with a form
#Include sub Folder or not
subfolders = forms.alert("Do you want to include subfolders?", yes=True, no=True)
I experimented with both functions in regex and normal string. As I said, it's a bit faster with a normal string; however, regex allows for much better handling of various edge cases, with improved management of spaces between hyphens. So I think it’s best to stick with regex
Here is what I came up with in regex :
def rename_pdf_re(old_name):
try:
# Step 1: Delete everything before and including "Sheet -", with space management
new_name = re.sub(r"^.*?Sheet\s*-\s*", "", old_name)
# Step 2: Make the part after the last hyphen before the period capitalized
new_name = re.sub(r"\s*-\s*(.*)\.", lambda m: "-{}.".format(m.group(1).upper()), new_name)
# Step 3: Normalize all other hyphens surrounded by spaces to single hyphens with no spaces (Optional)
new_name = re.sub(r"\s*-\s*", "-", new_name)
except:
return False
# Test new_name validity
if new_name == old_name or new_name == "":
return False
else:
return new_name
Here is what I came up with normal string (does include step 3, which replaces all spaces and hyphens with just a hyphen) :
def rename_pdf_str(old_name):
#The non regex fonction may be litle bite faster It is more complex to manage the different cases with or without spaces
try:
index_sheet = old_name.find("Sheet - ")
if index_sheet != -1:
new_name = old_name[index_sheet + len("Sheet - "):]
new_name = new_name[:new_name.find("-")+1] + " " + new_name[new_name.find("-") + 1:].strip().upper()
else:
return False
except:
return False
if new_name == old_name or new_name == "":
return False
else:
return new_name
As I mentioned, I added the option to also handle subfolders.
if not forms.alert("Do you want to include subfolders?", yes=True, no=True):
#No sub Folder treatment proced for basefolder
sheetcount = sheetcount + process_folder(basefolder)
else:
# Search for pdf files in base folder and sub folder, add folder who have any in the list
folders_to_process = []
for root, dirs, files in os.walk(basefolder):
if any(f.lower().endswith(".pdf") for f in files):
folders_to_process.append(root)
if not folders_to_process:
forms.alert("No .pdf file found in these folder")
#list not empty proced for each folder
else:
for folder in folders_to_process:
sheetcount = sheetcount + process_folder(folder)
# let user know how many sheets have been renames
forms.alert("{0} FILES RENAMED.".format(sheetcount))
I don't know yet how to push or propose a modification in the project code !? I will search for it and commit next month !
Happy New Year and enjoy your holidays if you have some ! 🎉✨🎄
Thanks, everyone, for the effort you put in !
Hi @Malouche-git , thank you for the contribution!
Indeed the regex way is slower, but it can be optimized by compiling the patterns with re.compile as stated in the documentation:
using
re.compile()and saving the resulting regular expression object for reuse is more efficient when the expression will be used several times in a single program.
prefix_stripper = re.compile(r"^.*?Sheet\s*-\s*")
capitalizer = re.compile(r"\s*-\s*(.*)\.")
normalizer = re.compile("\s*-\s*")
def capitalizer_repl(match):
return "-{}.".format(m.group(1).upper())
def rename_pdf(old_name):
new_name = prefix_stripper.sub("", old_name)
new_name = capitalizer.sub(capitalizer_repl, new_name)
return normalizer.sub("-", new_name)
I'm against returning False in the function on an error or no substitution; here we are in a self contained script at it doesn't matter that much, but still: it's a bad practice to return different types and having the caller figure out why a "rename_pdf" function returned False instead of a string as it would expect.
just move the try/except and the last checks outside of the function.
we can keep the empty new_name check inside the function and return a ValueError
def rename_pdf(old_name):
new_name = prefix_stripper.sub("", old_name)
new_name = capitalizer.sub(capitalizer_repl, new_name)
new_name = normalizer.sub("-", new_name)
if not new_name:
raise ValueError("Renaming results in an empty string.")
return new_name
Also, the file operations can be simplified with pathlib; it is a python 3 library, but it has been ported to python 2.7 and is available in pyrevit.
from pathlib import Path
# ...
dir_pattern = "**/" if forms.alert("Do you want to include subfolders?", yes=True, no=True) else ""
for pdf_file in Path(basefolder).glob("{}*.pdf".format(dir_pattern)):
try:
new_name = rename_pdf(pdf_file.stem)
except (ValueError, re.error):
continue
new_path = pdf_file.with_name("{}.pdf".format(new_name))
pdf_file.rename(new_path)
Finally, to send the proposed changes you have to:
- fork this repository (in the start page of the repo you'll se a "Fork" button)
- since this is a single-file modification, you can just use the web editor to change the contents of the scripts and commit them
- once the commit has been created, you'll see a "Contribute" button, click it and select "Open a pull request".
You can read the official github documentation, if you want to do the "full-contributor" way and work on a local copy synced to your repository
Original PR by @Malouche-git already merged.