Extensible Storage & CPython method overloading problems (Dynamo 2.10 & Python3)

Hi folks,

please excuse the crossposting but I thought someone might have come up with a solution to this problem:

I want to implement Extensible Storage in an addin I’m coding using CPython and I am hitting a wall when I try to set a value of a field in an schema using a template parameter in the function call. It seems this is an issue with PythonNet ¿has anybody experienced it?

As a workaround: what would you suggest?

I copy below the code and error from my post in dynamobim forums:

component = UnwrapElement(IN[0])

TransactionManager.Instance.EnsureInTransaction(doc)

def create_schema():
    schema_guid = System.Guid("DF3BBCC1-4D4D-4A01-B444-F9722814F9CE")
    schema_builder = SchemaBuilder(schema_guid)
    schema_builder.SetReadAccessLevel(AccessLevel.Public)
    schema_builder.SetWriteAccessLevel(AccessLevel.Public)
    schema_builder.SetSchemaName("TestSchema")
    schema_builder.SetDocumentation("Test schema to store ids")
    field_builder = schema_builder.AddSimpleField("ParentComponentID", System.String)
    schema = schema_builder.Finish()
    return schema

def add_schema_instance(schema, rvt_element):
    field = schema.GetField("ParentComponentID")
    schema_instance = Entity(schema)
    schema_instance.Set[System.String](field, "ID0004")  # Problem is here
    rvt_element.SetEntity(schema_instance)
    

TransactionManager.Instance.TransactionTaskDone()

schema = create_schema()

When I try to port it to CPython, this same code crashes with exception

Warning: TypeError : No method matches given arguments for Set: (<class ‘Autodesk.Revit.DB.ExtensibleStorage.Field’>, <class ‘str’>)

Would you guys recommend to code the extensible storage bit in C#? That way I could call it with something like what Ehran mentioned here: https://github.com/eirannejad/pyRevit/issues/143

maybe try fixing these like so ?
field_builder = schema_builder.AddSimpleField("ParentComponentID", String)

schema_instance.Set(field, "ID0004")

That doesn’t work, same error. It seems that PythonNET does not handle the template parameters (the type of the schema field)

Is there any other notation we can use for that?

can you put the code and the error in the thread please. @IvanPajares

Hi @Jean-Marc

this is the PyRevit script I am testing:

#!python3

"""Extensible Storage test script with CPython engine"""
# pyRevit info
__title__ = 'TEST'
__author__  = 'Iván Pajares Sánchez'

import clr
from Autodesk.Revit.DB import *
import Autodesk.Revit.UI as rui
from Autodesk.Revit.DB.ExtensibleStorage import *

clr.AddReference("System")
import System

# VARIABLES
doc = __revit__.ActiveUIDocument.Document
uidoc = __revit__.ActiveUIDocument

def create_schema():
    schema_guid = System.Guid("DF3BBCC1-4D4D-4A01-B444-F9722814F9CE")
    schema_builder = SchemaBuilder(schema_guid)
    schema_builder.SetReadAccessLevel(AccessLevel.Public)
    schema_builder.SetWriteAccessLevel(AccessLevel.Public)
    schema_builder.SetSchemaName("TestSchema")
    schema_builder.SetDocumentation("Test schema to store ElementIds")
    field_builder = schema_builder.AddSimpleField("ParentComponentID", System.String)
    schema = schema_builder.Finish()
    return schema

def add_schema_instance(schema, rvt_element):
    field = schema.GetField("ParentComponentID")
    schema_instance = Entity(schema)
    schema_instance.Set(field, "ID0004")
    return rvt_element.SetEntity(schema_instance)

t = Transaction(doc, "Extensible Storage")
try:
    t.Start()

    component = doc.GetElement(ElementId(3352908))
    schema = create_schema()
    add_schema = add_schema_instance(schema, component)
    t.Commit()
except Exception as ex:
    rui.TaskDialog.Show("ERROR", str(ex))
    t.RollBack()
finally:
    t.Dispose()

Error returned is:

TypeError : No method matches given arguments for Set: (<class 'Autodesk.Revit.DB.ExtensibleStorage.Field'>, <class 'str'>)

In the Revit API documentation it states that we need to pass a template parameter with the type of the field to be set:

Apparently, from the answers in DynamoBim forums, PythonNET cannot handle that yet

As I said above, the following call in IronPython is indeed passed correctly to the method:
schema_instance.Set[System.String](field, "ID0003")

The reason for my inquiry is that the addin I am working on is completely developed in CPython to use external libraries not available for IronPython (mainly IfcOpenShell and others)

Thanks for your interest!

after reading your topic on dynamo forum, my answer below might be off topic @IvanPajares


removing the top of your boiler plate including the Cpython encoding + changing the element id to relate to something in my model did work


# removed the top part of your code 

import clr

from Autodesk.Revit.DB import *

import Autodesk.Revit.UI as rui

from Autodesk.Revit.DB.ExtensibleStorage import *

clr.AddReference("System")

import System

# VARIABLES

doc = __revit__.ActiveUIDocument.Document

uidoc = __revit__.ActiveUIDocument

def create_schema():

    schema_guid = System.Guid("DF3BBCC1-4D4D-4A01-B444-F9722814F9CE")

    schema_builder = SchemaBuilder(schema_guid)

    schema_builder.SetReadAccessLevel(AccessLevel.Public)

    schema_builder.SetWriteAccessLevel(AccessLevel.Public)

    schema_builder.SetSchemaName("TestSchema")

    schema_builder.SetDocumentation("Test schema to store ElementIds")

    field_builder = schema_builder.AddSimpleField("ParentComponentID", System.String)

    schema = schema_builder.Finish()

    return schema

def add_schema_instance(schema, rvt_element):

    field = schema.GetField("ParentComponentID")

    schema_instance = Entity(schema)

    schema_instance.Set(field, "ID0004")

    return rvt_element.SetEntity(schema_instance)

t = Transaction(doc, "Extensible Storage")

try:

    t.Start()

    component = doc.GetElement(ElementId(12826)) #id to be changed or passed by a selection of some sort

    schema = create_schema()

    add_schema = add_schema_instance(schema, component)

    t.Commit()

except Exception as ex:

    rui.TaskDialog.Show("ERROR", str(ex))

    t.RollBack()

finally:

    t.Dispose()

also watchout not to reuse the GUID on something else later on

Yes it works but using the IronPython engine
I want to use CPython (PythonNet), hence the problem.

I have to either port all the code to ironpython or find a way to be able to integrate the extensible storage code done in IronPython with the CPython base. Does that make sense?

1 Like