pyrevitlabs/pyRevit

[Bug]: cpython 3.8.5 shows "<class 'int'>" instead of "<class 'BuiltInParameter'>" enum member of BuiltInParameter

Opened this issue · 4 comments

ay-ex commented

✈ Pre-Flight checks

  • I don't have SentinelOne antivirus installed (see above for the solution)
  • I have searched in the issues (open and closed) but couldn't find a similar issue
  • I have searched in the pyRevit Forum for similar issues
  • I already followed the installation troubleshooting guide thoroughly
  • I am using the latest pyRevit Version

🐞 Describe the bug

When running these (apart from python3 shebang identical) snippets:
in ironpython:

import clr
clr.AddReference("Autodesk")

from Autodesk.Revit.DB import BuiltInParameter as Bip

import sys


print(sys.version_info)
print(sys.implementation.name)
print("code: `type(Bip.LEVEL_IS_BUILDING_STORY)`")
print(type(Bip.LEVEL_IS_BUILDING_STORY))

and cpython

#! python3
import clr
clr.AddReference("Autodesk")

from Autodesk.Revit.DB import BuiltInParameter as Bip

import sys


print(sys.version_info)
print(sys.implementation.name)
print("code: `type(Bip.LEVEL_IS_BUILDING_STORY)`")
print(type(Bip.LEVEL_IS_BUILDING_STORY))

with pyrevit engines ironpython 3.4.0 and cpython 3.8.5 I get different type results:

  • on ironpython: <class 'BuiltInParameter'> (as expected)
  • on cpython: <class 'int'> (not as expected)

this prevents me from using level.get_Parameter(Bip.LEVEL_IS_BUILDING_STORY)
to access ui-language-agnostic parameter values for built-in-parameters.

image

⌨ Error/Debug Message

if a parameter access is use with the above in cpython like so: 

doc = __revit__.ActiveUIDocument.Document
print(doc.ActiveView.GenLevel.get_Parameter(Bip.LEVEL_IS_BUILDING_STORY))


the resulting error is as expected (it cannot find an overload with int as method argument):

CPython Traceback:
TypeError : No method matches given arguments for get_Parameter: (<class 'int'>)
 File "C:\ProgramData\pyRevit_BH\sandbox.extension\pyRevit_BH.tab\Sandbox.panel\Sandbox.pulldown\bip_test_cPy3_Sandbox.pushbutton\bip_test_cPy3_Sandbox_script.py", line 16, in <module>
print(doc.ActiveView.GenLevel.get_Parameter(Bip.LEVEL_IS_BUILDING_STORY))

pyRevitLabs.PythonNet
at Python.Runtime.Runtime.CheckExceptionOccurred()
 at Python.Runtime.PyScope.Exec(String code, IntPtr _globals, IntPtr _locals)
 at Python.Runtime.PyScope.Exec(String code, PyDict locals)
 at PyRevitLabs.PyRevit.Runtime.CPythonEngine.Execute(ScriptRuntime& runtime)

# ------

I get the impression that in cpython we get the integer back from the BuiltInParameter Enum instead of the expected BuiltInParameter class object.

♻️ To Reproduce

  1. create new pyrevit button with the above snippets, for both ironpython and cpython.
  2. run them and observe the different type output.

⏲️ Expected behavior

receiving BuiltInParameter type for both python interpreter versions.

🖥️ Hardware and Software Setup (please complete the following information)

* win 10 (19045)
* rvt 2024 (24.1.0.66)
* pyrevit 4.8.14
  * ironpython 3.4.0
  * cpython 3.8.5

Additional context

there is a discussion on the forum describing the same problem:
https://discourse.pyrevitlabs.io/t/getting-parameters-of-element-in-cpython/3011
so far there is no solution mentioned there.

ay-ex commented

I just tested it on pyRevit 4.8.16 , cpython 3.8.5 - same issue there.

ay-ex commented

a possible workaround is to go via forge_type_id - it is not pretty though: 🤔

  • via direct access:
from Autodesk.Revit.DB import BuiltInParameter as Bip
from Autodesk.Revit.DB import ForgeTypeId, ParameterUtils


doc = __revit__.ActiveUIDocument.Document
param = doc.ActiveView.GenLevel.GetParameter(ForgeTypeId(ParameterUtils.GetParameterTypeId(getattr(Bip, "LEVEL_IS_BUILDING_STORY")).TypeId))
# instead of just:
# param = doc.ActiveView.GenLevel.get_Parameter(Bip.LEVEL_IS_BUILDING_STORY)

but already the next step: param.StorageType is your yet another enum that only returns int.

  • via creating dict as lookup table for BuiltInCategory:
print(35*"-")
print("bic: forge_type_id_by_cat_name")
from Autodesk.Revit.DB import BuiltInCategory as Bic
from Autodesk.Revit.DB import Category


forge_type_id_by_cat_name = {}

for bic_cat in dir(Bic):
    if bic_cat.startswith("OST_"):
        bic_name = bic_cat.split("OST_")[-1]
        bic = getattr(Bic, bic_cat)
        if Category.IsBuiltInCategoryValid(bic):
            ftid = Category.GetBuiltInCategoryTypeId(bic)
            forge_type_id_by_cat_name[bic_name] = ftid.TypeId
            # print(ftid.TypeId)
        else:
            pass
            # print("skipped: ", bic_name)

print(f"found {} ftids".format(len(forge_type_id_by_cat_name)))
# for k, v in sorted(forge_type_id_by_cat_name.items()):
#     print(k, v)
  • via creating dict as lookup table for BuiltInParameter:
print(35*"-")
print("bip: forge_type_id_by_param_name")
from Autodesk.Revit.DB import BuiltInParameter as Bip
from Autodesk.Revit.DB import ForgeTypeId, ParameterUtils


forge_type_id_by_param_name = {}

skip_names = {
    "INVALID",
    "Overloads",
}

for bip_name in dir(Bip):
    # print(bip_name)
    if bip_name in skip_names:
        continue
    if bip_name.endswith("__"):
        continue
    bip = getattr(Bip, bip_name)
    if "method" in str(bip) or "function" in str(bip):
        continue
    ftid = ParameterUtils.GetParameterTypeId(bip).TypeId
    # print(bip_name, ftid)
    forge_type_id_by_param_name[bip_name] = ftid

print("found {} ftids:".format(len(forge_type_id_by_param_name)))
# for k, v in sorted(forge_type_id_by_param_name.items()):
#     print(k, v)

so ideally there could be a fix with which we could access enums in pyRevit cpython the same way as in ironpython. 🤔

Hi @ay-ex, sorry for the delay in the response.

This is a known bug of the version of pythonnet currently used by pyrevit. It was solved in version 3.0.
We're trying to update it alongside the upgrade to the .net8 sdk to support revit 2025.
As this was not our project originally and we're not super c# experts, we're trying to do the best we can to reach this goal.
The upcoming pyrevit version 5 should fix this issue (no ETA yet, unfortunately).

ay-ex commented

hi @sanzoghenzo ,
no worries and thank you for the notification. 🙂
looking forward to pyRevit 5.