CPython Key Error importing pyRevit module

This is my first attempt at a CPython script and I am having issues using the pyRevit module. here is my code:

#!python3

from pyrevit import DB, revit

collector = DB.FilteredElementCollector(revit.doc).OfCategory(DB.BuiltInCategory.OST_Areas).ToElements()
print (len(collector))

When i run it in Revit, I get the following error:

CPython Traceback:
KeyNotFoundException : KeyError
 at IronPython.Runtime.PythonDictionary.GetItem(Object key)
 at IronPython.Runtime.PythonDictionary.get_Item(Object[] key)

It definitely seems to have to do with importing pyRevit. I tried running the “Test CPython Command” in Dev tools, and it printed out everything up until the point where it imports pyRevit, and then throws the same error:

1 Like

i’m having the same issue.

Can’t really figure it out since i’m to novice in this kind of code.

Hey guys!

To start things off, I’m recent on using cypthon (and even pyRevit itself) but anyways, i’ll try to help out.
So first things first, Cpython is limited to what Ironpython can do from what concerns built-in functions from pyRevit. (somebody correct me if I’m wrong).

To call in Cypthon, this should do the trick with an example of getting levels;

from Autodesk.Revit.DB import * #Import Everything from DB (Good for beginners and development)
doc   = __revit__.ActiveUIDocument.Document

collector_lvl = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).WhereElementIsNotElementType().ToElements()

Best of luck !

well i’m looking for a replacement on the .forms module.

This are easto use for file selection and other GUi’s.
I could make them with tkinter but that doesn’t work with Cpython:
Tkinter module not found - Tools - pyRevit Forums (pyrevitlabs.io)

Hi guys,

From what I have found, this is happening basically because the pyRevit module isn’t compatible with CPython. To get around this, when I need to use CPython packages, but also have to do a lot of “Revity” things, I will make my script in the standard IronPython 2.7, and then use the “subprocess” module to call a separate python script that runs in CPython. That way, I can do the revit stuff, send data to the CPython script, and then get back data. This also helps solve the GUI issue, as I can still build XAML interface that works with the IronPython.

2 Likes

Great news, thanks Dennis! If it is not much to ask, could you give a small code example to see how it works? I looked at the Documentation but not sure I understood it entirely and how to apply it to our case. Thank you in advance!

FYI: I should have solved this specific error, see this issue comment on github.
If you try it, please confirm (there or here) that it works for you so I can edit the official code.

Unfortunately this patch doesn’t completely solve the error because a new, different error shows up.

The bottom line is (as you already figured out): pyrevitlib is not yet compatible with cpython
From previous Ehsan statement on github, the work to make it compatible was delayed at least until pythonnet released the v3 version; When I have enough time I’ll try to help in making this switch.

In the meantime:

  • If you can, find a way around pyrevit things; if you look at the library code you can extract only the things that you need and there’s a big chance that they work (except WPF/XAML windows!)
  • @AlexanderVDB: tkinter it is not packaged with pyrevit, but if you have a CPython environment installed you could try to sys.path.append("path/to/site-packages") and then import it from there. I’m not guaranteeing it works but it is something worth trying. Of course this will not be portable!
  • @DennisGoff this is a good workaround; care to share an howto so that less experienced users can kickstart their projects?
  • @ValquirPacheco while you wait for the howto, let me tell you that: the subprocess lets you call any other program installed in your system; I suppose that Dennis uses it to call an external python interpreter subprocess.run(["path/to/python.exe", "my_script.py", "any", "argument", "you", "need"], ...); obviously you cannot interact with revit API in that script, but you can exchange data, for example by reading and writing to the standard input/output (what you usually write or read in a command prompt) or a file.
    If you really need to interact with revit, you can leverage pyrevit REST routes and create custom ones if you need them (with IronPython, or following the previous instructions).
2 Likes

Here is a simple example of the subprocess in action:
The script that runs in Python 2.7 sends a message to the script that runs in python 3.8. The 3.8 accesses the arguments from the subprocess command via sys.argv[1, 2 ,3 etc] depending on how many arguments you pass through, and then prints a response

### PYTHON 2.7 SCRIPT ###
import subprocess

pathToScript = "path to python 3.8 script"
message = "My name is Python 2.7"
cmd = ['python', pathToScript, message]

# Create the subprocess startup info object This prevents the black terminal window from popping up when running hte subprocess
startup_info = subprocess.STARTUPINFO()
startup_info.dwFlags |= subprocess.STARTF_USESHOWWINDOW

# Run the command and capture the output
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=startup_info)

output, error = process.communicate()
print output
### Python 3.8 Script ###
import sys

message = sys.argv[1]

name = message.split("My name is ")[1]
response = "Hello " + name + ", My name is Python 3.8"
print(response)

The “output” variable that is printed from the 2.7 script will be whatever is printed from the 3.8 script. Hope this helps!

2 Likes

I hope one day I can repay both of you, @sanzoghenzo and @DennisGoff for the help and clarifications :smile:! If there is anything related to France or Brazil that I could help with, feel free to contact me! valquirn@gmail.com

note that the subprocess calls can be simplified (and it is the recommended way by the python documentation):

result = subprocess.run(cmd, capture_output=True, startupinfo=startup_info)
print result.stdout

here result is an instance of the CompletedProcess class, so you can check the returncode among other things.

3 Likes

Thank you so much for this - it allowed me to utilize a xaml interface with IronPython, but delegate the retrieval of project information from SmartSheet to a subprocess which I could bring back in as a list to populate the form fields and set parameters. Works great!

1 Like