Getting Enum values in CPython3 returns integers

Hi there,

When retrieving values of a System.Enum with GetValues() it returns an integer type with CPython3.
In IronPython 2.x, the returned values where of the underlying type.

Can someone explain why it has changed ?
Can I still get the values rightly typed in CPython3 ?
If so, how can I do it (in C# your can “cast” the type you need but I have no idea how to do this with CPython)

Here is a code sample and the result :

excel=IN[0]
list=[]
types=System.Enum.GetValues(ParameterType)
for i in excel:
	for j in types:
 		if str(j)==str(i):
			list.append(j)
OUT = list

Thanks to anyone helping me sorting out this hairpulling issue !

Kind regards,

1 Like

It looks like you just want the object type. That’s much easier to get:

2 Likes

@Yorick ,

thats true i am struggeling too … can`t solve it…

there are still problems with libraries a.s.o.

import sys
import clr
import System

excel = IN[0]
enum_type = System.Enum.GetUnderlyingType(System.Enum)

try:
    enum_value = System.Enum.Parse(enum_type, excel)
    OUT = [enum_value]
except ValueError:
    OUT = []

i tried this but also not working

KR

Andreas

@Nick_Boyts ,

that works

@Nick_Boyts ,

But in revit 2024… it does not work :frowning:

when i add “something” it works :frowning:

KR

Andreas


That’s odd. Not sure what that’s about but you can always make this multi-line code.

Try OUT = [x.__class__() for x in IN[0]]

Might not need the parenthesis… not at my CPU to check.

1 Like

@jacob.small

i got no error

KR

Andreas

Omit the ().

2 Likes

To help further than what you’re seeing so far we’d need a full code sample, including your imports. Something may be off up there which is causing only half of what we need exposed (or exposing too much).

Hi, @Yorick and welcome
it’s an issue with PythonNet 2.5.x see here

there is an example of workaround here

3 Likes

Hi everyone,
Thank you for your interest in this topic.

I feel like I should give you guys a bit more context. I am trying to “upgrade” a working Dynamo graph written mostly in IronPython 2 to CPython3.

This graph takes an ExcelFile from my users and :

  • creates new shared parameter
  • loads the parameter into the project
  • OR updates the categories associated to it if the parameter already exists (for it is the only writable property of the ElementBinding)

To do so, at some point in my code, I need to create an ExternalDefinitionCreationOptions which takes two arguments, the parameter name which is a String and the parameter type which is an object of type Autodesk.Revit.DB.ParameterType

My process is to read the string from the Excel file, to collect all ParameterType with System.Enum.GetValues(ParameterType), go through all of them comparing the excel input with the ParameterType name (through str(ParameterType) ) so I can store the ParameterType of class Autodesk.Revit.DB.ParameterType in a new list

I would like to get the same result in CPython3 that I get in IronPython2

The issue seems to be the one @c.poupin pointed out but the proposed workaround in the other topic doesn’t work for me.

With :

dictParameterType = dict(zip(System.Enum.GetValues(ParameterType), System.Enum.GetNames(ParameterType)))

I get this error :

Unable to cast an object of type 'System.Int64' into a type 'System.String'

And building the dictionary the other way around :

dictParameterType = dict(zip(System.Enum.GetNames(ParameterType), System.Enum.GetValues(ParameterType)))

Returns once again a System.Int64 object when getting values (eg: dictParameterType["Text"] returns 1 )

In the pythonnet GitHub, someone gives this workaround :

from Tests import TestClass, TestEnum
a = TestClass()
a.EnumMethod[TestEnum](TestEnum.FirstValue)

But I am unable to figure out how I can adapt it to my case. Especially because Autodesk.Revit.DB.ParameterType is the class I am looking for and the Enum at the same time

Trying other proposed solutions did not work so far.
I hope someone can help me figure this one out and thanks again for your help !

I’m not sure if there is a simple solution with PythonNet 2.5

maybe you can try to use the new constructor with ForgeTypeId

ExternalDefinitionCreationOptions(
	string name,
	ForgeTypeId dataType

instead of

ExternalDefinitionCreationOptions(
	string name,
	ParameterType type

FYI the issue is solved with PythonNet 3.x.x

1 Like

I may be misunderstanding, but it might be that you need to overhaul things more wholistically, as ExternalDefinitionCreationOptions requires a name (which will be the name of the parameter) and a ForgeTypeId, which doesn’t require an enumerator to construct…

Well you can use ForgeTypeId or ParameterType.

I am looking into using ForgeTypeId indeed, it’s just a bit less convenient to convert a string like ‘Text’ or ‘Integer’, which my users can easily understand, to a ForgeTypeId.

But yeah it’s the workaround I am heading for.
Thing is it won’t help much if I encounter the same issue with other enumerators (BuitlInCategory I am looking at you)

Will post my code once I finalized it :wink:

So here is the result with ForgeTypeId workaround :

For copy/paste :

#Retrieves user input
excel=IN[0]

#Creates a dictionary with returned integer as key and ParameterType Name as value
pTypesN = System.Enum.GetNames(ParameterType)
pTypesT = System.Enum.GetValues(ParameterType)
dictParamType = {}

for i in range(0,len(pTypesN)) :
	dictParamType[str(pTypesT[i])] = pTypesN[i]

#Gets all ForgeTypes
ForgeTypes = SpecUtils.GetAllSpecs()

pTypesFromForge = []

#Creates a sorted list of corresponding ParamterType Name to each ForgeType where it exists, if not inserts null value
for ft in ForgeTypes :
    try :
        ptKey = str(SpecUtils.GetParameterType(ft))
        pTypesFromForge.append(dictParamType[ptKey])
    except :
        pTypesFromForge.append(None)

#From user input parameter type name, stores the corresponding ForgeTypeId in a new list to be used with ExternalDefinitionCreationOptions
userForgeType = []

for uInput in excel :
    ind = pTypesFromForge.index(uInput)
    #For demo purpose get the TypeId which is human readable
    userForgeType.append([ForgeTypes[ind],ForgeTypes[ind].TypeId])


OUT = userForgeType

Because user Inputs are ParameterType names, getting a null should never happen.
But the mapping between ParameterType names and ForgeTypeId is not unique :

  • Custom : leads to a bunch of ForgeTypeId
  • Length : leads to both Length and sheetLength
  • Angle : leads to Angle and siteAngle

Given a bit of luck, the ones my users would use are the first ones, so index() works for me.

Hopefully this can help others as well.

3 Likes

i used Clockwork’s Python node for testing, see uploaded image, what’s the difference and how to convert those 2 outputs i.e. FloorPlan vs Autodesk.Revit.DB.ViewPlan?

Hi,

here a workaround

if one day PythonNet 3 is implemented, this workaround will no longer be necessary…

cool! thanks c.poupin