Cant sucessfully implement the IFailuresPreprocessor interface

Hello, i’ve been trying to develop a code to ignore all warnings that is generated when i try to join multiple walls in a project

it seems like i have an issue with the implementation of the IFailuresPreprocessor Interface

class JoinFailureAdvancedHandler(IFailuresPreprocessor):
	def PreprocessFailures(failuresAccessor):
		failMessages = failuresAccessor.GetFailureMessages()
		if failMessages.Count == 0:
			return FailureProcessingResult.Continue
		transName = failuresAccessor.GetTransactionName()
		if transName == "Wall Join Operation":
			if failuresAccessor.GetSeverity() == FailureSeverity.Warning:
				for currentMessage in failMessages:
					failuresAccessor.ResolveFailure(currentMessage)
				failuresAccessor.DeleteAllWarnings()	
				return FailureProcessingResult.ProceedWithCommit
				
		return FailureProcessingResult.Continue

It is not deleting the warnings generated in the document when i try to join then. And it even blocks me from ignoring then… only allowing me to cancel the warning, thus, rolling back the join operation transaction

t = Transaction(doc)
t.Start("Wall Join Operation")
joinFailOp = t.GetFailureHandlingOptions()
handler = JoinFailureAdvancedHandler()
joinFailOp.SetFailuresPreprocessor(handler)
t.SetFailureHandlingOptions(joinFailOp)
.
.
.
#Join Operation
.
.
.
t.Commit()

the join operation code has been tested and it works… But what it doesnt work is the handler for the joining operation.

Does anyone has any idea of what is wrong with my code?
Thank you !

Do you guys have any thoughts about this ?
Thanks
@jacob.small
@Kulkul
@john_pierson
@Dimitar_Venkov

hello @ramoon.bandeira
you are missing the instance of the class on the method (self), suddenly a parameter is missing from the PreprocessFailures fonction (method)

2 examples of classes with a double wall creation

class DuplicateMarkSwallower(IFailuresPreprocessor):
	def PreprocessFailures(self, a):
		failures = a.GetFailureMessages()
		for  f in failures:
			id = f.GetFailureDefinitionId()
			if BuiltInFailures.OverlapFailures.WallsOverlap == id:
				a.DeleteWarning(f)	
		return FailureProcessingResult.Continue
		
class CreateFailureAdvancedHandler(IFailuresPreprocessor):
	def PreprocessFailures(self, failuresAccessor):
		failMessages = failuresAccessor.GetFailureMessages()
		if failMessages.Count == 0:
			return FailureProcessingResult.Continue
		transName = failuresAccessor.GetTransactionName()
		if transName == "Wall Create Operation":
			if failuresAccessor.GetSeverity() == FailureSeverity.Warning:
				for currentMessage in failMessages:
					failuresAccessor.DeleteWarning(currentMessage)	
		return FailureProcessingResult.Continue

level = UnwrapElement(IN[1])	
TransactionManager.Instance.ForceCloseTransaction()
with Transaction(doc) as t:
	t.Start("Wall Create Operation")
	failureOptions = t.GetFailureHandlingOptions()
	failureOptions.SetFailuresPreprocessor(CreateFailureAdvancedHandler())
	#OR exemple with GetFailureDefinitionId()
	#failureOptions.SetFailuresPreprocessor(DuplicateMarkSwallower())
	t.SetFailureHandlingOptions(failureOptions)
	#
	#
	line = Line.CreateBound(XYZ.Zero, 10 * XYZ.BasisX)
	for i in range(0,2):
		wall = Wall.Create(doc, line, level.Id, False)
	#
	#
	t.Commit()
4 Likes

Oh my god… i have no words to describe how thankfull i am for this.

This was driving me nuts. And as always… it was such a simple thing!

Thanks a lot man!!!

1 Like

Hi Cyril!
Just tried that code in Dynamo CPython3 Mode. It hangs on failureOptions.SetFailuresPreprocessor(CreateFailureAdvancedHandler())

The exception seems to indicate that CreateFailureAdvancedHandler() needs an argument to function. Works just fine in IronPython2 without one. Any idea on how to make it work in CPython3?

Still, got some practice with Objects in Python. Silver lining.

Would need to see the error itself, but you may need to define the namespace for the class.

Unfortunately, this workaround work one time per Revit session

you can get around this by importing uuid, and generate a unique guid for each run. Not ideal, but it works.

1 Like

Thanks, I’ll have a look.

I imagine using the code above in Python 3 would reproduce the error in any environment.
The exception was “Class needs exactly 1 argument” or something like that.

Not at my cpu so I can’t test, but see if the above resolves things for you.

it works, but becomes unstable when there are too many calls (Memory increased and Revit crash)

this may be solved with PythonNet 3x, alternately we can use IronPython3 engine

Can’t say I have seen such an issue - I’m pretty sure I have utilized the same graph with the uuid call for over 4 hours of development lasting upwards of 200 times while authoring without any problems.

interesting, I will redo a test of loading families with IFamilyOptions interface

1 Like

It’s frustrated to see that this might be again a Pythonversion problem (related to CPython3) .

@c.poupin @jacob.small Thx for all your constructive sharing of experience for this topic.
Is there any new from your sides to handle this “interface takes exactly one argument.” problem :).

Adding a “namespace” indeed only works once per Revit, which is really annoying when I need to handle multiple .rvt (doc)

Use a randomly generated GUID for each run, and in my experience the code runs consistently and repeatedly with that change.

2 Likes

I’m struggling with implementing the IFailuresPreprocessor interface and i’m not sure what i’m missing.

i’ve reviewed this thread and the one on stackoverflow. I’m trying to transcribe this example of using the SketchEditScope class to modify a floor. For some reason this seemed to work with Revit 2024 and Dynamo 2.18, but i’ve upgraded to Revit 2024.2 and Dynamo 2.19 and i get the following error:

TypeError: No constructor matches given arguments: ()['File "<string>", line 96, in <module>\n]

I think i have the syntax for defining the class correct (?) and have included the __namespace__ attribute as recommended. I tried explicitly defining a constructor but i don’t think this makes sense for this example because there are no additional arguments.

my python code:

#------------------IMPORTS------------------
import clr
#  Include these lines to access python modules
import sys
import uuid
#sys.path.append(r'C:\Users\mclough\AppData\Local\python-3.8.3-embed-amd64\Lib\site-packages') # update this path with your user data

#clr.AddReference('ProtoGeometry')
#from Autodesk.DesignScript.Geometry import PolyCurve

clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
from Autodesk.Revit.Exceptions import * # enable this for debugging

#clr.AddReference('RevitAPIUI')
#from Autodesk.Revit.UI import *

clr.AddReference('System')
from System.Collections.Generic import List

# There are namespace overlaps between ProtoGeometry and RevitNodes
# Be sure to address these discrepancies
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

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

doc = DocumentManager.Instance.CurrentDBDocument
uidoc=DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument

#-----------------FUNCTIONS-----------------
toList = lambda x: x if isinstance(x, list) else [x]
cast = lambda x: x.ToRevitType()

def polycurve_to_CurveArray (polycurve):
	#boundariesLoop = List[CurveLoop]()
	boundariesLoop = CurveArray()
	crvs = polycurve.Curves()
	
	for c in crvs:
		#trace.append("    found curve as a "+str(type(c)))
		c_converted = c.ToRevitType()
		boundariesLoop.Append(c_converted)
		#trace.append("    added as a "+str(type(c_converted)))
	#trace.append("   boundaries as "+str(type(boundariesLoop)))
	
	return boundariesLoop

#-----------------CLASSES-------------------

class FailuresPreprocessor(IFailuresPreprocessor):
    __namespace__ = str(uuid.uuid4())
    
    def PreprocessFailures(self, failuresAccessor):
        return FailureProcessingResult.Continue

#------------------INPUTS-------------------

#Preparing input from dynamo to revit
element = UnwrapElement(IN[0])
curves = IN[1]

result = []
trace = []

boundariesLoop = List[CurveLoop]()
#fpp = FailuresPreprocessor()

#-------------------MAIN--------------------
#Do some action in a Transaction
#outer = curves.pop(0)
#inner = curves

bl = [polycurve_to_CurveArray (c) for c in curves]

skid = element.SketchId
sketch = doc.GetElement(skid)
ses = SketchEditScope(doc, "modify-floor-curves")
if ses.IsPermitted:
    ses.Start(skid)
    
    t = Transaction(doc,"update-sketch-boundary")
    t.Start()
    d_elems = sketch.GetAllElements()
    [doc.Delete(e) for e in d_elems]
    
    trace.append(bl)
    [doc.Create.NewModelCurveArray(b, sketch.SketchPlane) for b in bl]
    t.Commit()
    ses.Commit(FailuresPreprocessor())
else:
    trace.append(f"SketchEditScope is not permitted")
    trace.append(f"IsModifiable: {doc.IsModifiable}")
    trace.append(f"IsReadOnly: {doc.IsReadOnly}")
    trace.append(f"IsSketchEditingSupported: {ses.IsSketchEditingSupported}")
    
#------------------OUTPUT-------------------
OUT = ses, sketch, result, trace

Hi @mclough

since revit 2024.1 PythonNet bridge (PythonNet 2.5) for the CPython3 engine was compiled with NetStandard2.0 as target (in order to prepare for the transition to Net8) which broke several features of the old version of PythonNet which mainly uses the .NetFramework as support.

The integration of Pythonnet 3.0 will undoubtedly correct the problem in the future (to be checked, because the integration of PythonNet 3 does not seem easy)