[Revit 2024] In Python node in Dynamo doc.LoadFamily does not accept loadoptions anymore.

[Revit 2024] In Python node in Dynamo doc.LoadFamily does not accept loadoptions anymore.

j.podlaha
Explorer Explorer
3,352 Views
7 Replies
Message 1 of 8

[Revit 2024] In Python node in Dynamo doc.LoadFamily does not accept loadoptions anymore.

j.podlaha
Explorer
Explorer

When I call `familyDoc.LoadFamily(doc, FamilyLoadOptions())` in Revit 2024 I get following error: 

 

    TypeError: interface takes exactly one
    argument [' File "«string›", line 97, in
    <module>\n']

 

This code used to work, but the method seems to no longer accept the second argument, which doesn't seems correct. Is this my bug or a bug in Revit 2024 Python API or maybe a API change, or am I missing something else?? 

 

----

 

I originally posted the question with more context in dynamobim forums: 

https://forum.dynamobim.com/t/how-do-i-define-a-family-parameter-in-revitapi-2024-using-python-block...

 

Entire source code: 

import sys
import clr
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

## Input parameters
familyName = IN[0]
family = UnwrapElement(IN[1])


## Test if family already contains the parameter
def familyHasParameter(family, parameterName):
        currentParams = family.FamilyManager.GetParameters()
        for k in range(len(currentParams)):
                if currentParams[k].Definition.Name == parameterName:
                        return True
        return False


## Get Family document from family instance
def getFamilyDoc(fam, doc):
        if isinstance(fam, Autodesk.Revit.DB.FamilyInstance):
                return (Document.EditFamily(doc, fam.Symbol.Family))
        else:
                if fam.IsInPlace == False:
                        return (Document.EditFamily(doc, fam))


## Implementation of family load options - always override existing family
class FamilyLoadOptions(IFamilyLoadOptions):
        def     OnFamilyFound(self, familyInUse, overwriteParameterValues):
                overwriteParameterValues = True
                return True

        def     OnSharedFamilyFound(self, sharedFamily, familyInUse, source, overwriteParameterValues):
                source = FamilySource.Family
                overwriteParameterValues = True
                return True


###########################################################


TransactionManager.Instance.TransactionTaskDone()
TransactionManager.Instance.ForceCloseTransaction()

doc = DocumentManager.Instance.CurrentDBDocument


print("-- Editing family: " + familyName)
familyDoc = getFamilyDoc(family, doc)


TransactionManager.Instance.EnsureInTransaction(doc)
trans = Transaction(familyDoc, "Edit family params")
trans.Start()


parameterName = "TEST"
groupTypeId = Autodesk.Revit.DB.GroupTypeId.Data
specTypeId = Autodesk.Revit.DB.SpecTypeId.Current
isInstance = True

if familyHasParameter(familyDoc, parameterName):
    print(("-- Parameter already exists on family: " + parameterName))
else:
    print(("-- Adding parameter to family: " + parameterName))
    familyDoc.FamilyManager.AddParameter(parameterName, groupTypeId, specTypeId, isInstance)
    
trans.Commit()
trans.Dispose()

TransactionManager.Instance.TransactionTaskDone()
TransactionManager.Instance.ForceCloseTransaction()

print("-- Saving family to project")
familyDoc.LoadFamily(doc, FamilyLoadOptions())
    
TransactionManager.Instance.TransactionTaskDone()
TransactionManager.Instance.ForceCloseTransaction()

 

0 Likes
Accepted solutions (1)
3,353 Views
7 Replies
Replies (7)
Message 2 of 8

architect.bim
Collaborator
Collaborator
Accepted solution

Hi!

This is definetly CPython3 problem. I've made a short test. And LoadFamily method accepts load options just fine if you use IronPython2 in Dynamo instead:

 

f_doc = doc.EditFamily(family)

TransactionManager.Instance.EnsureInTransaction(f_doc)
f_doc.FamilyManager.NewType("Test")
TransactionManager.Instance.TransactionTaskDone()

f_doc.LoadFamily(doc, FamilyLoadOptions())

 

 Worked fine in Revit 2024. But when I switched to CPython3 the same error happened.


Maxim Stepannikov | Architect, BIM Manager, Instructor
Message 3 of 8

j.podlaha
Explorer
Explorer

Hey, thanks a lot for helping to figure this out. How should I handle this issue now? Is there a bugtracker to report this to? Switching back to IronPython2 currently doesn't work for me (I struggle to do the switch in current Revit). 

Message 4 of 8

architect.bim
Collaborator
Collaborator

Hi!

I think the best way to report the bug is in their repository on Github. But first, read this article, maybe this bug is already listed there.

As for using IronPython2 - it works fine. If you write more details about what you're having trouble with, maybe I can help. In my opinion it's a better option than waiting for Dynamo CPython3 developers to fix all the bugs.


Maxim Stepannikov | Architect, BIM Manager, Instructor
Message 5 of 8

jacob.small
Autodesk Support
Autodesk Support

I've shared this a few times in the Dynamo forum, but apparently not to a wide enough audience, so I'll post this here as well. 

This isn't an issue with CPython, but more likely with Python 3 entirely as IronPython3 fails similarly. As such reporting to the Dynamo GitHub wont' help much - you'll have to go to the Python one instead. The reason this fails now is that classes required a namespace to pass to the .net environment cleanly. If you provide a namespace things should work without issue for the first run.

However namespaces cannot be duplicated in the same kernal, and as the class with it's namespace has been passed once already it either needs to be disposed after run (haven't tested this as it isn't something which I have needed in my work of late) or a unique namespace needs to be generated for each run (works without issue for a few hundred runs while developing code anyway).

 

A non-Revit centered version of the problem and the same solution can be found here: https://stackoverflow.com/a/50462691

Message 6 of 8

b_neterebskiiFSAA9
Community Visitor
Community Visitor

I find out that it is possible to handle this problem using new Namespace everytime. E.g. with uuid function. Here is the example:

 

import uuid

class WarningFailureMessageClass(IFailuresPreprocessor):
    __namespace__ = str(uuid.uuid4())

    def PreprocessFailures(self, failuresAccessor):
        fail_acc_list = failuresAccessor.GetFailureMessages()
        for failure in fail_acc_list:
            severity = failure.GetSeverity()
            if severity == FailureSeverity.Warning:
                failuresAccessor.DeleteWarning(failure)
        return FailureProcessingResult.Continue


def suppress_warnings(transaction):
    fail_opt = transaction.GetFailureHandlingOptions()
    preprocessor = WarningFailureMessageClass()
    fail_opt.SetFailuresPreprocessor(preprocessor)
    transaction.SetFailureHandlingOptions(fail_opt)
0 Likes
Message 7 of 8

e.j.nap
Observer
Observer

Works like a charm! I would never have come up with this solution.

 

It seems a bit strange though. It deliberatly obfuscates the namespace for the .NET interpreter. Could this cause some sort of degredation of the .NET interpreter functionality in the long run, or are these new namespaces not stored after a run where this class is instantiated?

0 Likes
Message 8 of 8

LoganAC34
Advocate
Advocate

@b_neterebskiiFSAA9 wrote:

I find out that it is possible to handle this problem using new Namespace everytime. E.g. with uuid function. Here is the example:

 

import uuid

class WarningFailureMessageClass(IFailuresPreprocessor):
    __namespace__ = str(uuid.uuid4())

    def PreprocessFailures(self, failuresAccessor):
        fail_acc_list = failuresAccessor.GetFailureMessages()
        for failure in fail_acc_list:
            severity = failure.GetSeverity()
            if severity == FailureSeverity.Warning:
                failuresAccessor.DeleteWarning(failure)
        return FailureProcessingResult.Continue


def suppress_warnings(transaction):
    fail_opt = transaction.GetFailureHandlingOptions()
    preprocessor = WarningFailureMessageClass()
    fail_opt.SetFailuresPreprocessor(preprocessor)
    transaction.SetFailureHandlingOptions(fail_opt)

Unless i'm doing it wrong, using a uuid for the namespace always crashes Revit for me after 20-30 iterations. I've tried moving the 'family_load_options = FamilyLoadOptions()' line outside of the loop and it does the same thing. I also tried setting a static non-uuid string for the name space like the stackoverflow post and still the same thing. Nothing works for me for long. It's not the other parts of my code either, because if i run it without trying to load the family back into the project, everything works fine.

 

class FamilyLoadOptions(IFamilyLoadOptions):
    """A Class implementation for loading families"""
    __namespace__ = str(uuid.uuid4())

    def OnFamilyFound(self, familyInUse, overwriteParameterValues):
        """Defines behavior when a family is found in the model."""
        overwriteParameterValues = True
        return True

    def OnSharedFamilyFound(self, sharedFamily, familyInUse, source, overwriteParameterValues):
        """Defines behavior when a shared family is found in the model."""
        source = FamilySource.Project
        # source = FamilySource.Family
        overwriteParameterValues = True
        return True

## This is in a 'for' loop
family_load_options = FamilyLoadOptions()
famDoc.LoadFamily(doc, family_load_options)
0 Likes