Object does not implement IFailuresPreprocessor

Hi,

Running a graph which I have previously run successfully, but now I get this message for most families… I’m running in 2023 with CPython3, I don’t believe this should fail?

object does not implement IFailuresPreprocessor

I’m trying to background open families and return any errors which are generated.

Any assistance gratefully received!

Mark

import clr
import sys
import System
from System.Collections.Generic import List, IList, Dictionary

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

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *
from Autodesk.Revit.UI.Events import ViewActivatedEventArgs, ViewActivatingEventArgs, DialogBoxShowingEventArgs 

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

projDoc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = DocumentManager.Instance.CurrentUIApplication.Application


my_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments)
pf_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86)
sys.path.append(pf_path + '\\IronPython 2.7\\Lib')
import logging
import traceback

## Start create logger Object ##
logger = logging.getLogger("DimissDialog")
# set to  DEBUG
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s :: %(levelname)s :: %(funcName)s :: %(message)s')
# create handler 
file_handler = logging.FileHandler(my_path + '\\' + projDoc.Title + 'DimissDialog.log', mode='w')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.disabled = False

			
def ControlledApplication_FailuresProcessing(sender, e):
	logger.info("ControlledApplication_FailuresProcessing Event")
	global dict_corrupted
	global currentFamilyName
	global currentFamilyId
	try:
		fas = e.GetFailuresAccessor()
		fas.DeleteAllWarnings()
		fma = fas.GetFailureMessages()
		for fa in fma:
			logger.debug(("failure Description", fa.GetDescriptionText()))
			if fa.GetSeverity() != FailureSeverity.Warning:
				dict_corrupted.append([currentFamilyName, fa.GetDescriptionText()])
				#if fa.GetDefaultResolutionCaption() == "Remove Constraints":
				if fas.IsFailureResolutionPermitted(fa, FailureResolutionType.UnlockConstraints):
					fa.SetCurrentResolutionType(FailureResolutionType.UnlockConstraints)
					fas.ResolveFailure(fa)	
					e.SetProcessingResult(FailureProcessingResult.ProceedWithCommit)					
				else:
					fa.SetCurrentResolutionType(FailureResolutionType.DeleteElements)
					fas.ResolveFailure(fa)
					e.SetProcessingResult(FailureProcessingResult.ProceedWithCommit)

	except Exception as ex:
		logger.exception(ex)

			

class FailureAdvancedHandler(IFailuresPreprocessor):
	# create a dictionnary to store families with warnings
	dict_Warning = Dictionary[System.String, List[System.String]]()
	def __init__(self, famDoc):
		self._famDoc = famDoc
		
	def PreprocessFailures(self, failuresAccessor):
		failMessages = failuresAccessor.GetFailureMessages()
		if failMessages.Count == 0:
			return FailureProcessingResult.Continue
		transName = failuresAccessor.GetTransactionName()
		try:
			self.__class__.dict_Warning.Add(self._famDoc.Title + " " + str(currentFamilyId), List[System.String]([m.GetDescriptionText() for m in failMessages]))
			# if alert is an warn remove it
			if failuresAccessor.GetSeverity() == FailureSeverity.Warning:
				for currentMessage in failMessages:	
					failuresAccessor.DeleteWarning(currentMessage)					
				return FailureProcessingResult.Continue
			# if alert is an error resolve it (by delete elements or by split group etc...)       
			elif failuresAccessor.GetSeverity() != FailureSeverity.Warning:				
				for currentMessage in failMessages:
					failuresAccessor.SetCurrentResolutionType(FailureResolutionType.DeleteElements)
					failuresAccessor.ResolveFailures(currentMessage)
					return FailureProcessingResult.ProceedWithCommit
			else:
				pass
		except Exception as e:
			logger.exception(ex)
		return FailureProcessingResult.Continue

# Retrieve all families that are not system families in current doc
fam_types = FilteredElementCollector(projDoc).OfClass(Family)

errors = []
dict_warnings = None
dict_corrupted = []#Dictionary[System.String, System.String]()
delegate = System.EventHandler[FailuresProcessingEventArgs]( ControlledApplication_FailuresProcessing )
app.FailuresProcessing += delegate

for f in fam_types:
	if (f.IsEditable):
		currentFamilyName = f.Name + " : " + str(f.Id)
		try:
			TransactionManager.Instance.ForceCloseTransaction()
			famDoc = projDoc.EditFamily(f)
			t = Transaction(famDoc, 'Test')
			t.Start()
			failureOptions = t.GetFailureHandlingOptions()
			handler = FailureAdvancedHandler(famDoc)
			failureOptions.SetFailuresPreprocessor(handler)
			t.SetFailureHandlingOptions(failureOptions)

			if famDoc.IsFamilyDocument:
				famManager = famDoc.FamilyManager
				for familyType in famManager.Types:
					famManager.CurrentType = familyType
					famDoc.Regenerate() 
		except Exception as ex:
			logger.exception(ex)
			dict_corrupted.append([currentFamilyName, str(ex)])
		else:
			t.Commit()
			t.Dispose()
			famDoc.Close(False)
			famDoc.Dispose()

dict_warnings = FailureAdvancedHandler.dict_Warning
app.FailuresProcessing -= delegate

OUT = dict_corrupted, dict_warnings

to implement a class in CPython 3 you need to define the namespace. Each namespace must be unique, and can only be defined once. This means that you can generate a GUID and get a result one time, but subsequent runs will fail like you are now. To do so add this right after you define the class:

__namespace__ = str(uuid.uuid4())

Utilizing an IronPython method would be preferable (ie: IronPython 3), but better still is converting to a Zero Touch node.

2 Likes

Thanks Jacob, very useful…

I tried this, but it didn’t work? Did i put it in the wrong place? :slight_smile:

IronPython and Zero touch aren’t options for me right now.


class FailureAdvancedHandler(IFailuresPreprocessor):
	# create a dictionnary to store families with warnings
	dict_Warning = Dictionary[System.String, List[System.String]]()
	def __init__(self, famDoc):
		self._famDoc = famDoc
		self.__namespace__ = str(uuid.uuid4())

Hi,

Net Interfaces are buggy in PythonNet 2.5.x, there are some explanations about this issue
here

and here

you can try with @jacob.small 's solution or this one

import time  

class FailureAdvancedHandler(IFailuresPreprocessor):
    #
    __namespace__ = f"FailureAdvancedHandler_{int(time.time())}"
	# create a dictionnary to store families with warnings
	dict_Warning = Dictionary[System.String, List[System.String]]()
	def __init__(self, famDoc):
		self._famDoc = famDoc
		# rest of code

but it’s not stable (Revit may crash) so it would be better to use Ironpython (2 or 3) or build a .Net lib

2 Likes