Pyrevit: CPython does not understand Revit API classes

Created on 1 Feb 2021  路  3Comments  路  Source: eirannejad/pyRevit

Describe the bug

I have this set up using Revit 2021.2, pyrevit 2.7.7, cpython 3.7.2, IronPython 2.7.3.

I think that _script.py modules loaded with #! python3 make us lose the properties of RevitAPI elements in IronPython.

Bug 1: .GetType() vs isinstance()

I used to be able to compare elements like this in IronPython:

ref = uidoc.Selection.PickObject(UI.Selection.ObjectType.Face, "Select a planar face")
face = elem.GetGeometryObjectFromReference(ref)
if face.GetType() != DB.PlanarFace:
    raise Exception("%s (%d) is not a planar face" % (ref.Name, ref.Id.IntegerValue))

If this module (or the launching _script.py) is run with #! python3 I must check with isinstance:

# ... same as above
if not isinstance(face, DB.PlanarFace):
    raise Exception("%s (%d) is not a planar face" % (ref.Name, ref.Id.IntegerValue))

Bug 2: __enter__ not defined for DB.Transaction element
I am making a transation in a _script.py run with #! python3.

try:
        with DB.Transaction(doc, "do something") as t:
            t.Start()
            try:
               # my actions 
                t.Commit()
            except Exception as ex:
                print("failed transaction because of '%s'" % ex)
                t.RollBack()
except Exception ex2:
    print("hell raised by: %s")

getting hell raised by: __enter__ error.

If I remove the with-statement all goes well.

try:
     t = DB.Transaction(doc, "do something")
     t.Start()
     try:
         # my actions 
         t.Commit()
     except Exception as ex:
         print("failed transaction because of %s" % ex)
         t.RollBack()
except Exception ex2:
    print("hell raised by: %s")

I wouldn't like to change my code completely, because with gave me a good flow control.

Bug 3: operator- in DB.XYZ

Same situation: my _script.py launched with #! python3.

My code is this:

try:
    a = DB.XYZ(1.0, 2.0, 0.0) # notice that the CPython / IronPython coexistence 
                                           # forces me to pass doubles to the constructor or the point will be null
    b = DB.XYZ(2.0, 1.0, 0.0)
    c = b - a
    print("b-a = %s" % c)
except Exception as ex:
   print("hell raised by %s" % ex)

The error is hell raised by TypeError : unsupported operand type(s) for -: 'XYZ' and 'XYZ'

Question

Most helpful comment

The intent has always been to modify the existing python code to run on cpython as well. The issue is that the cpython engine connecting the cpython runtime to dotnet environment is also under development and is not as advanced as IronPython. I'd highly suggest sticking with IronPython and use cpython Only when you need cpython packages. for now. I'm actively working on the pythonnet implementation so we'll hopefully see improvements in the future

All 3 comments

@amastrobera with is a python construct. It expects the type in front of it, DB.Transaction in your example, to implement the __enter__ and __exit__ magic methods. But DB.Transaction is a type from Revit API and is not designed to work specifically with python so it does not implement these methods.

This is the correct approach:

 t = DB.Transaction(doc, "do something")
 try:
     t.Start()
     # my actions 
     t.Commit()
 except Exception as ex:
     print("failed transaction because of %s" % ex)
     t.RollBack()

In IronPython I use a python type that I have created that wraps the DB.Transaction. See code here

You can try importing this into cpython (might fail) from pyrevit import revit
Or create a light version of this for your scripts

Hi @eirannejad,

1) Importing pyrevit.revit.db

I tried from pyrevit.revit import db and it fails (like you predicted).

CPython Traceback:
TypeError : object does not implement IExternalEventHandler
 File "C:\Users\angel\developement\revit-plugins\pyRevit\extensions\pyRevit.extension\pyRevit.tab\Creator.panel\Road.pulldown\StraightRoad.pushbutton\create_road_straight_script.py", line 23, in <module>
from pyrevit.revit import db
 File "C:\Users\angel\AppData\Roaming\pyRevit-Master\pyrevitlib\pyrevit\revit\__init__.py", line 24, in <module>
 from pyrevit.revit import events
 File "C:\Users\angel\AppData\Roaming\pyRevit-Master\pyrevitlib\pyrevit\revit\events.py", line 101, in <module>
 FuncAsEventHandler(unregister_exec_handlers, purge=False)

If I import the single module like from pyrevit.revit.db import transaction as dbt, all goes well.

2) calling IronPython Revit API fails still
I still have the problem with operator- for DB.XYZ. I saw you have partially implemented some Autodesk.Revit.DB elements, but not all of them ... I could carry on translating Revit (IronPy) API into CPy, like you are doing in pyrevit.revit.db and even contribute to your repo ... but for what purpose ? I might end up re-writing the whole API just to make CPy understand it.

Is this the reason why you wrote the revit-python-wrapper and pyrevit.revit.db ?

3) [question] how are people migrating to CPython?

Code that worked in IronPython ceases to do so when #! python3 is used. The root cause might be some misunderstanding in interpretation. Can this be fixed ? I cannot think all programmers are rewriting huge chunks of code just to be able to incorporate numpy in their modules ...

The intent has always been to modify the existing python code to run on cpython as well. The issue is that the cpython engine connecting the cpython runtime to dotnet environment is also under development and is not as advanced as IronPython. I'd highly suggest sticking with IronPython and use cpython Only when you need cpython packages. for now. I'm actively working on the pythonnet implementation so we'll hopefully see improvements in the future

Was this page helpful?
0 / 5 - 0 ratings