[HowTo] FindFailed handler: General error handling --- 1.1.3 +

Asked by alex on 2018-06-25

************************* This works after having fixed some issues:

Using
FindFailed.setHandler(ffhandler)
takes care, that every Region created thereafter inherits the setting (handler will be visited on FindFailed)

In Jython/Jruby scripting:
If you want this behaviour for the standard SCREEN object too, you either have to issue a
use()
after the global setting or use
setFindFailedHandler(ffhandler)

The latter one is the only to use, if you want the behaviour only for the global SCREEN object.

Be aware:
If you do not want your script to abort after returning from the handler, you have to issue
event.setResponse(SKIP)
in the handler before the return.
Depending on what you have done in the handler, one of the other options RETRY or PROMPT might make sense as well.

------------------------------------------------------------------------------------------------------------

Let's assume I have a rather lengthy SikuliX script that can basically fail at any point - usually find failed.
Instead of modifying the whole script to catch every possible error, my idea was to use some kind of "supervisor" script instead.
The supervisor calls the script and if anything goes wrong there, the GUI in question is reset to its initial state by the supervisor which then calls the script again.
I got this idea working, but of course every time a find failed occurs in the called script everything comes to a stop.
What I would need is a way to simply exit a script (so the supervisor can take over) every time a find failed occurs. Is this possible?

"setFindFailedResponse()" doesn't seem to have the proper parameters for what I have in mind.
Setting it to "SKIP" won't do it because if an error occurs, the GUI HAS to be reset to its initial state.

Edit: tried using setFindFailedHandler:
setFindFailedHandler(resetGUI)
def resetGUI():
    #do stuff
#rest of the script

Still, the script just stops when encountering a FindFailed error instead of calling my resetGUI routine. In addition to that, I still have the problem that I would need to start the script at the beginning, no matter where an error occured.

Edit2: I am running SikuliX version 1.1.2 on Windows10. Yet even putting "setFindFailedResponse(SKIP)" in the header, the script still throws an exception when an image is not found...
...apparently, setFindFailedResponse gets overwritten by setFindFailedHandler. But still, my handler function is not executed when a FindFailed occurs.

Question information

Language:
English Edit question
Status:
Solved
For:
Sikuli Edit question
Assignee:
No assignee Edit question
Solved by:
RaiMan
Solved:
2018-06-28
Last query:
2018-06-28
Last reply:
2018-06-28

This question was reopened

  • 2018-06-27 by alex
RaiMan (raimund-hocke) said : #1

ok, understood.

I have to check. May take some hours ;-)

RaiMan (raimund-hocke) said : #2

ok, the situation is a bit complicated and the current docs do not reflect that sufficiently, sorry.

In short for Python scripting:
- setFindFailedHandler and setFindFailedResponse are Region specific, which means they are only used, for the Region, they are specified for, when a search/FindFailed happens.
- when a FindFailed situation happens, first the handler is visited, if specified for the region object. Then the setting of the FindFailedResponse of that region object is obeyed (which in turn might have been modified before in a handler)
- response and/or handler can be specified globally (FindFailed. setFindFailedResponse and FindFailed. setFindFailedResponse), so that every region object created afterwards will inherit the global settings

I will revise the docs accordingly asap.

For Python scripting, there is one special thing to be aware:
see bug 1778855

alex (privatesnowball) said : #3

Since I am not using any regions at all I just assumed it would work for the whole screen. To be honest, I have not yet wrapped my hread around the concept of regions.

I already tried using FindFailed.setFindFailedHandler and the shortcuts for it mentioned in the docs. However, I get an error message if I do so. Not at the right PC now, but IIRC it said something about FindFailed does not have this attribute...will try to reproduce the error tonight.

Just to be sure: do I have to start using regions if I want to use setFindFailedHandler? I was really hoping that I don't have to dive into this.

alex (privatesnowball) said : #4

Accidentally clicked on "this solved my problem"

RaiMan (raimund-hocke) said : #5

--- do I have to start using regions if I want to use setFindFailedHandler?
No, you do not have to.

See the mentioned bug for a workaround.

I will again check with version 1.1.2 (I normally work with the latest 1.1.3 nightly)

RaiMan (raimund-hocke) said : #6

ok, this is due to the not-doced-problem:

FindFailed.setResponse(...)
FindFailed.setHandler(...)

and
FindFailed.getResponse()

are the respective methods.

RaiMan (raimund-hocke) said : #7

To be on the safe side with the current implementation, you should act so:

def ffhandler(event):
  print "in FindFailed handler"

FindFailed.setHandler(ffhandler) # set as global handler even for Regions
use() # reset the global SCREEN object to take over the handler setting

This must be inserted at the top of the main script.

If you want this behavior in any imported script (having "from sikuli import *" at the beginning), then those scripts need this also.

If you want to share the handler among scripts, then it must be in one script

# FindFailedHandling.sikuli
from sikuli import *
def ffhandler(event):
  print "in FindFailed handler"

and then in the script that needs it:
from sikuli import *
from FindFailedHandling import *
FindFailed.setHandler(ffhandler)
use()

in the main script the
from sikuli import *
is not needed

alex (privatesnowball) said : #8

I am using a single script for now. With the method you proposed in your last post, the handler routine is still not invoked on FindFailed. The script still aborts when FindFailed occurs.

alex (privatesnowball) said : #9

Kind of got it working by putting these lines on top of the script:
def ffhandler(event):
    resetGUI()
FindFailed.setHandler(ffhandler)
use()
setFindFailedHandler(ffhandler)

However I was surprised to learn that apparently even constructs like these
while(not exists(Pattern("1527938243237-3.png").similar(0.95),1.0)):
        #do stuff
count as a FindFailed. At least they invoke the handler script which renders all of this pretty useless for what I had in mind.

RaiMan (raimund-hocke) said : #10

def ffhandler(event):
    resetGUI()
FindFailed.setHandler(ffhandler)
use()

... should be sufficient

--- exists()
accepted - I have to find a way, to fix it.
I think, that the best thing would be, that the handler should know, wether it was an exists, so you can decide, to do nothing.
To generally switch of the visit of the handler with exists does not make sense, since other things than your idea a user might want to implement in the handler.

I will be back within 24 hours.

alex (privatesnowball) said : #11

That is exactly what I tried first. Did not invoke the handler script on a FindFailed.

I tend to disagree with you on the exsts() issue. Unlike other features like find or click, it does not abort the script with a FindFailed if the image is not found.

RaiMan (raimund-hocke) said : #12

--- Did not invoke the handler
ok, I have to check again and fix if needed

--- I tend to disagree with you on the exists() issue
I tried to find things, that supports my view - but found none.
So I will now switch off FindFailed handling with exists() completely and wait if someone complains about in the future.

Thanks for your cooperation.

alex (privatesnowball) said : #13

Thank you for your support and this awesome piece of software. Despite the issues i am still facing, it allowed me to do much more and in a shorter amount of time than any other automation software that I am aware of.

Best RaiMan (raimund-hocke) said : #14

Thanks for kind feedback.

--- Did not invoke the handler
I checked and have to admit you are right. Is fixed now.

--- exists() should not abort when coming back from FindFailed
I fixed this now.

This works:
Using
FindFailed.setHandler(ffhandler)
takes care, that every Region created threafter inherits the setting (handler will be visited on FindFailed)

In Jython/Jruby scripting:
If you want this behaviour for the standard SCREEN object too, you either have tu issue a
use()
after the global setting or use
setFindFailedHandler(ffhandler)

The latter one is the only to use, if you want the behaviour only for the global SCREEN object.

Be aware:
If you do not want your script to abort after returning from the handler, you have to issue
event.setResponse(SKIP)
in the handler before the return.
Depending on what you have done in the handler, one of the other options RETRY or PROMPT might make sense as well.

alex (privatesnowball) said : #15

Thanks RaiMan, that solved my question.

alex (privatesnowball) said : #16

Works like a charm with the latest build. Dankeschön!

RaiMan (raimund-hocke) said : #17

Alles Gute ;-)