How to do an easy not-found handling?

Asked by RaiMan

Anyone any idea how to get this into real world?

the background:

if you have a workflow like:
switchApp("app")
wait()
find()
click()
click()
type()
wait()
find()
click()
.... and so on

running in the IDE, you have some chance to see, when the script fails, what the cause may have been. When running as executable, you simply have to accept, that it stops working at all or goes into weird situations sometimes (there are questions and bug reports in this direction).

To make such a workflow more secure, you can intermix some python and logging, that checks at critical points, that we are still where we expect to be.

goOn = True
if find():
   click(find.region)
else:
   goOn = false
if goOn:
  ... further actions

You can use wait(), to only go on working if specific patterns are there, but wait has to be supplemented with some if/while constructs to trap the timeouts.
If you do this extensively, than you will come up with a script, that has 4 to 8 additional python statements for every Sikuli action. In german we say: you don't see the wood (my workflow), because of so many trees (+ python).

I would like to do something like that:

1. scenario (could be the default)
setTryEnvironment = "easy"
switchApp("app")
wait()
find()
click()
click()
type()

when I run the script: if the first not-found situation happens, the script stops and I get a message like this:
- not-found: the statement with line number
- a thumbnail with link to the picture that failed
- link to the screenshot with the last successful action

2. scenario
setTryEnvironment = "mature"
try:
  wait()
  find()
  click()
error:
  # not-found in the try block brings me here
  (x, y, z, a, b, c) = error.get() # get some predefined information about the not-found
  # do some evaluation and corrections and say
  continue # goes back to the point of error and continues or
  break # continues after the try block or
  exit # stops the script with a configurable message
# after the try
click()
type()

statements outside a try are handled as scenario 1.

3. scenario
setTryEnvironment = "off"
should be like scenario 2 but without the easy option.

In longer scripts, you can have more than one setTryEnvironment that start working from this point on.

Anyone any idea how to get this into real world?

Question information

Language:
English Edit question
Status:
Solved
For:
SikuliX Edit question
Assignee:
No assignee Edit question
Solved by:
RaiMan
Solved:
Last query:
Last reply:
Revision history for this message
RaiMan (raimund-hocke) said :
#1

found some answers on scenario 2 myself.

the standard python try-block works:

try: # the indented statements are watched for any errors
   wait()
   click()
   .... more statements

except: # this block is jumped into, if an error occurs
   popup("sorry, some error")

# here we go if everything works well
popup("keep on running")

There are some problems, that I will report as a bug
- except FindFailed: not possible, name error - only except: works fine
- error info with sys.exc_info() possible, but import traceback gives name error
- finally: does not work (syntax error)

example with all I found possible:
import sys, re
setThrowException(True) # has to be set, to get the FindFailed errors
myErr = False
expr=re.compile(r"edu.mit.csail.uid.FindFailed:\s(.*\s(\d*.png))") # relevant info

try:
   click( ) # this image will not be found

except:
   err = str(sys.exc_info()[1])
   m = expr.match(err)
   myErr = True
   if m:
      print "++++ FindFailed ++++ "+m.group(2)
      popup(m.group(1))

popup("all things running")

Revision history for this message
RaiMan (raimund-hocke) said :
#2

This is my actual version and it works like a charm:

def tOn():
   setThrowException(True)
def tOff():
   setThrowException(False)
import sys, re, traceback
reFindFailed = re.compile(r"edu.mit.csail.uid.FindFailed:\s(.*(\d*.png).*)")

setThrowException(True)
try:
# ------------------------------------ myHeader ends here

here I have my find(), click(), wait()
...
....
this is the workflow - everything has to be indented!

#------------------------------------ myFooter starts here
except:
   (errA, errB, errC) = sys.exc_info()
   serrC = traceback.extract_tb(errC)
   m = reFindFailed.match(str(errB))
   if m: # this is a FindFailed
      lastError="Error Line("+str(serrC[0][1])+"): "+m.group(1)
      msg = lastError
      print serrC
   else: # every other type of error
      print "++++ < Error > ++++"
      print errA, errB
      traceback.print_exc()
      msg = str(errA) +" --- "+str(errB)
   switchApp("Sikuli-IDE")
   popup(msg)
# ---------------- script ends here

What does it do?
I switch on, that find() throws a FindFailed exeption, when not found happens. I get a nice popup, telling me the line number, where the not found happened plus the filename of the searched pic (so I can look at without going into the code).

What do I get?
I simply write down my find(), click(), .... without ever testing the returns. If something goes wrong, the script stops and I know exactly where and why. I'm very happy with that.

comment on tOn() and tOff():
Sometimes its by intent during the workflow, that if something is not there makes the difference.
 tOff(); ret=wait(<image>); tOn() # these 1-liners are python and work in Sikuli
 if ret:
   ... my branch if found
 else:
   ... my branch if not found
tOn() and tOff() are simply shortcuts to save typing. the wait runs normally without raising the execption (thats what I want).

Revision history for this message
Tsung-Hsiang Chang (vgod) said :
#3

Thanks for your detailed report and idea.
I just committed a fix that fixes the namespace problem of FindFailed and also shows more helpful information for a FindFailed exception.

Revision history for this message
RaiMan (raimund-hocke) said :
#4

Everything I want to do possible with 0.9.9
(except wait() ambiguous: https://bugs.launchpad.net/sikuli/+bug/529025)
Thanks and looking forward to 0.10.