Supress Warnings

You know that we have to use Transactions to make changes to our projects. But what if we get warnings during Transactions and they slow us down?

Supress Warnings

You know that we have to use Transactions to make changes to our projects. But what if we get warnings during Transactions and they slow us down?

Summary

Why Supress Warnings?

When we work in Revit we often get annoying Warning messages or even Errors. And we have to interact with them. And while making an extra click might not seem like an issue, imagine you create a script where you go through 100 elements, and each one causes a warning which has to be addressed manually. And you have to click it for all 100 elements or Kill Revit process.

That's usually when people start asking, how do I avoid it. Including myself… But let's begin by creating a script that can produce a consistent warning message.

Create a Warning Example

You know when you update 'Mark' parameter in Revit, it gives you an annoying warning about duplicate parameter value. And the worst thing - you have to click on it…

That's exactly what we will do to create a warning so we can practice suppressing it.

It's not complicated and you already know how to select elements and update parameters.

First of all let's select walls by using ISelectionFilter and PickObjects.

#⬇️ Imports
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI.Selection import *

#📦 Variables
uidoc = __revit__.ActiveUIDocument
doc   = __revit__.ActiveUIDocument.Document

#🔬 ISelectionFilter - Walls
class WallFilter(ISelectionFilter):
    def AllowElement(self, elem):
        if type(elem) == Wall:
            return True

#👉 Pick Walls
ref_picked_walls = uidoc.Selection.PickObjects(ObjectType.Element, WallFilter())
picked_walls     = [doc.GetElement(ref) for ref in ref_picked_walls]

Let's also go through these walls and change Mark parameter one by one to the same value. This will trigger the warning.

Notice, that I've put my Transaction inside of a for-loop. I still recommend you to keep all your transactions outside of your loops. But for this lesson I want to get multiple warnings on each of the elements.

However, make sure you don't iterate through hundreds of elements (trust me…)

#⚠️ Create a Warning
for wall in picked_walls:
    # 🔓 Start Transaction
    t = Transaction(doc, 'Update Mark')
    t.Start()

    #⚠️ Warning 1
    p_mark = wall.get_Parameter(BuiltInParameter.ALL_MODEL_MARK)
    p_mark.Set('Warning 2')

    t.Commit()

This is perfect, because it will consistently give you this Duplicate Parameter Value warning, so we can actually test our warning suppressor multiple times before it works. However, keep in mind that we would need to make Mark value new to trigger the warning. Otherwise it just stays the same and doesn't produce a warning.

Here how it looks so far. Not bad!

How to react to Warnings?

Now we are ready to look at warnings.

First of all, keep in mind that it's quite an advanced topic, but we will only focus on how to suppress individual warnings without all nitty-gritty details. That way, it will be simple for anyone to follow along.

There are 2 steps.

1️⃣ Firstly, we need to create an Error Handler.

2️⃣ We need to set our Error Handler to the Transaction

1️⃣ Create Error Handler (IFailuresPreprocessor)

First of all we need to create class that will act as an error handler.

And we need to use IFailuresPreprocessor Interface for that. Interface means that it's a blueprint that should be used for creating a class that has built-in functionality. Similar to ISelectionFilter…

Here is how we can create this class by inheriting IFailuresPreprocessor Interface. Inside, there is only 1 method that we need to override, which is called - PreprocessFailures.

Here is the base:

#⚠️ Warning Handler - Base
class SupressMyWarnings(IFailuresPreprocessor):
    def PreprocessFailures(self, failuresAccessor):
        print('Error')

IFailuresPreprocessor Remarks:
This interface, if provided, is invoked when there are failures found at the end of a transaction. An instance of this interface can be set in the failure handling options of transaction object.

Set Error Handler to Transaction

Now we can go to our Transaction and assign this Error handler.

We need to do 3 steps:
1️⃣ Transaction.GetFailureHandlingOptions
2️⃣ FailureHandlingOptions.SetFailuresPreprocessor
3️⃣ Transaction.SetFailureHandlingOptions

Here is the code Snippet:

#⚠️ Create a Warning
for wall in picked_walls:
    # 🔓 Start Transaction
    t = Transaction(doc, 'Update Mark')
    t.Start()

    #⚠️ Warning 1
    p_mark = wall.get_Parameter(BuiltInParameter.ALL_MODEL_MARK)
    p_mark.Set('Warning 2')

    #💡 Assign Error Handler
    fail_hand_opts = t.GetFailureHandlingOptions()
    fail_hand_opts.SetFailuresPreprocessor(SupressWarnings())
    t.SetFailureHandlingOptions(fail_hand_opts)

    t.Commit()

This will make sure that whenever we get any Errors or Warnings during the Transaction, it will call our SupressWarnings Error Handler. Now we can test and see if it works, it should print 'Error' message that we wrote.

When you test it, you will also notice that you won't be able to click on OK button for the warning. This is because we assigned our Error Handler but we didn't specify that it can proceed with the warning, so Revit thinks that it should be cancelled and it blocked OK button. Let's fix that

Allow Warnings

To allow warnings in Revit we need to return FailureProcessingResult.Continue from PreprocessFailures method. This way Revit will allow us to accept warnings and commit the change in the project.

Here is the Snippet

#⚠️ Warning Handler - Base
class SupressMyWarnings(IFailuresPreprocessor):
    def PreprocessFailures(self, failuresAccessor):
        print('Error')
        return FailureProcessingResult.Continue
Set Error Handler to Transaction

Also keep in mind that if you will have any syntax or code errors in this class, Revit will just consider that it should cancel the transaction. And the worst part is that you won't get an error message to know what needs to be fixed. That's an issue during development.

So we will use try/except statements and print errors messages with traceback module. This way if you messed up something, Revit will print you an error message, and you will know at what line of code you should look and what went wrong. Here is how we do that.

#⚠️ Transaction Error Handler
class SupressWarnings(IFailuresPreprocessor):
    def PreprocessFailures(self, failuresAccessor):
        try:
            print(1/0)
        except:
            import traceback
            print(traceback.format_exc())

        return FailureProcessingResult.Continue

⚠️You can test it, because it will give you an error that it can't be divided by 0.

Supress All Warnings

Alright, we are ready to start suppressing warnings.

So far:
- We've create an error handler class
- Assigned it to Transaction
- And made sure that we can see traceback errors

To Suppress Warnings, we need to read them and make sure that the severity is a Warning and not an Error, because errors can't be suppressed!. We will use failuresAccessor argument in the PreprocessFailures method for that.

We can get all failure messages by using GetFailureMessages.

Then for each failure we can read its:
- Severity (GetSeverity)
- Description (GetDescriptionText)
- Fail Id (GetFailureDefinitionId)

And then if failure has a severity of FailureSeverity.Warning, then we can use DeleteWarning method to supress it.

Here is the updated SupressWarnings class.

#⚠️ Transaction Error Handler
class SupressWarnings(IFailuresPreprocessor):
    def PreprocessFailures(self, failuresAccessor):
        try:
            failures = failuresAccessor.GetFailureMessages()

            for fail in failures: #type: FailureMessageAccessor
                severity    = fail.GetSeverity()
                description = fail.GetDescriptionText()
                fail_id     = fail.GetFailureDefinitionId()

                if severity == FailureSeverity.Warning:
                    failuresAccessor.DeleteWarning(fail)
                    print('Captured: {}'.format(description))

        except:
            import traceback
            print(traceback.format_exc())

        return FailureProcessingResult.Continue

Now if we try it out, you will see that we have suppressed all warnings and it has printed about it in the console.

Suppress Only Specific Warnings

This is already great as we can suppress all warnings. However, it might not be the best practice, as you might suppress unexpected warnings too. So instead of that it's better to check what kind of warning this you get, and if it's something that you expected, then you would suppress it.

And we can do that by comparing FailureDefinitionId that we got as fail_id like this:

if fail_id == BuiltInFailures.GeneralFailures.DuplicateValue:
    print('Captured: {}'.format(description))
    failuresAccessor.DeleteWarning(fail)

There are a lot of classes for different warnings in Revit API. In this case I want to suppress: BuiltInFailures.GeneralFailures.DuplicateValue warning.

💡 It's a bit tricky to find the right class. But usually I use Google, Chat GPT and just scroll through Revit API docs and I find it. I thin there should be a better way to do so, but I don't use it as often so this dirty method usually works just fine. But if you find a better way, please share with the rest of us too.

Create more warnings

Lastly, let's create another warning by deleting the selected wall, so rooms will become NotEnclosed. This way we can try to suppress 2 different warnings.

We will have to add doc.Delete inside of the Transaction like this:

#⚠️ Warning 2
doc.Delete(wall.Id)

And we have to add to Error Handler that RoomNotEnclosed should also be suppressed.

if fail_id == BuiltInFailures.RoomFailures.RoomNotEnclosed:
    print('Captured: {}'.format(description))
    failuresAccessor.DeleteWarning(fail)

Now our error handler will make sure that the following warnings are suppressed:

👀 Now you can test if you've suppressed your warnings.

Final Code

Now, let's put it all together, so we all have the same code that's in the video:

# -*- coding: utf-8 -*-
__title__   = "05.05 - Supress Warning"
__doc__ = """Date    = 22.03.2024
_____________________________________________________________________
Description:
Learn how to Supress Warnings with Revit API.
_____________________________________________________________________
Author: Erik Frits"""

# ╦╔╦╗╔═╗╔═╗╦═╗╔╦╗╔═╗
# ║║║║╠═╝║ ║╠╦╝ ║ ╚═╗
# ╩╩ ╩╩  ╚═╝╩╚═ ╩ ╚═╝ IMPORTS
#==================================================
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI.Selection import *


# ╦  ╦╔═╗╦═╗╦╔═╗╔╗ ╦  ╔═╗╔═╗
# ╚╗╔╝╠═╣╠╦╝║╠═╣╠╩╗║  ║╣ ╚═╗
#  ╚╝ ╩ ╩╩╚═╩╩ ╩╚═╝╩═╝╚═╝╚═╝
#==================================================
uidoc = __revit__.ActiveUIDocument
doc   = __revit__.ActiveUIDocument.Document

#--------------------------------------------------

#🔬 ISelectionFilter - Walls
class WallFilter(ISelectionFilter):
    def AllowElement(self, elem):
        if type(elem) == Wall:
            return True

#⚠️ Transaction Error Handler
class SupressWarnings(IFailuresPreprocessor):
    def PreprocessFailures(self, failuresAccessor):
        try:
            failures = failuresAccessor.GetFailureMessages()

            for fail in failures: #type: FailureMessageAccessor
                severity    = fail.GetSeverity()
                description = fail.GetDescriptionText()
                fail_id     = fail.GetFailureDefinitionId()

                if severity == FailureSeverity.Warning:

                    if fail_id == BuiltInFailures.GeneralFailures.DuplicateValue:
                        print('Captured: {}'.format(description))
                        failuresAccessor.DeleteWarning(fail)

                    if fail_id == BuiltInFailures.RoomFailures.RoomNotEnclosed:
                        print('Captured: {}'.format(description))
                        failuresAccessor.DeleteWarning(fail)


                    else:
                        print('Warning: {}'.format(description))
        except:
            import traceback
            print(traceback.format_exc())

        return FailureProcessingResult.Continue

#👉 Pick Walls
ref_picked_walls = uidoc.Selection.PickObjects(ObjectType.Element, WallFilter())
picked_walls     = [doc.GetElement(ref) for ref in ref_picked_walls]



#⚠️ Create a Warning
for wall in picked_walls:
    # 🔓 Start Transaction
    t = Transaction(doc, 'Update Mark')
    t.Start()

    #⚠️ Warning 1
    p_mark = wall.get_Parameter(BuiltInParameter.ALL_MODEL_MARK)
    p_mark.Set('Warning 2')

    #⚠️ Warning 2
    doc.Delete(wall.Id)

    #💡 Assign Error Handler
    fail_hand_opts = t.GetFailureHandlingOptions()
    fail_hand_opts.SetFailuresPreprocessor(SupressWarnings())
    t.SetFailureHandlingOptions(fail_hand_opts)

    t.Commit()

HomeWork

Create an Error Handler as I've shown in the video, and test it by creating and most importantly - Suppressing Warnings.

There is no need to go crazy here, just set the same Mark Value to create a Warning, and then make sure that you can suppress it.

And remember, you don't need to include it in every Transaction you make. Only use it when you start getting warnings and it becomes annoying.

⌨️ Happy Coding!

Questions:

I can't use Interface in CPython...

I can't use Interface in CPython...

Discuss the lesson:

P.S. This discord window is controlled with a bot and sometimes you might experience connection issues. If that happened, open Discord server with an app and look for this lesson's channel. Alternatively you could give it a try later in the browser.

Sorry for that, I am still figuring it out.
- Erik

P.S. This discord window is controlled with a bot and sometimes you might experience connection issues. If that happened, open Discord server with an app and look for this lesson's channel. Alternatively you could give it a try later in the browser.

Sorry for that, I am still figuring it out.
- Erik

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API