switchLogin Script from client event keeps throwing error

I'm running a script in my program that will switch the login to a default login after a certain time period, and although it does exactly what it's supposed to, (logs out and in correctly) it throws an error: Error running function from fpmi.system.invokeAsynchronous or fpmi.system.invokeLater (I've tried both) with the details: TypeError: 'bool' object is not callable

The script looks like this:
currentUser=system.security.getUsername()

import system.util
import time
import system.security

print(currentUser)

try:
if currentUser != "u******":
try:
time.sleep(5)
system.util.invokeAsynchronous(system.security.switchUser("u********", "p*********"))
except Exception as e:
system.util.getLogger("AutoLogOut").warn("Error in invokeLater: " + str(e))
except Exception as e:
system.util.getLogger("AutoLogOut").warn("Error in invokeLater: " + str(e))

using asterisks in place of user/pword info. Ideally, I'd like it to not throw any errors, but I can't figure out what the issue even is, I've tried many different workarounds.

system.util.invokeAsynchronous requires a bare function object. No parenthesis. If you are on recent version of Ignition, you can give your function args and keyword args to invokeAsynchronous for it to deliver inside the new thread. If that isn't available, you have to use closures or functools.partial to create a temporary callable that embeds your arguments. Search this forum for later.py for utility functions that produce suitable closures.

The error you are getting is that your function is running before the thread is spawned (because you included parens), and its return value is what is being given to invokeAsynchronous.

Would the parenthesis cause the same issue on invokeLater? I'd prefer to use that one for simplicity, left it as async because it didn't change behavior. I'm kind of confused on the parenthesis though, the autofill for the invoke functions both include parenthesis so I'm not quite sure which ones you're referring to. Thanks either way.

Yes, same requirement for invokeLater. Study the examples in the manual.

To summarize:
To run this function:

somePackage.someFunction(1, 2, arg3, arg4, arg5=5, arg7=23)

in a background thread, you do this:

system.util.invokeAsynchronous(somePackage.someFunction, [1, 2, arg3, arg4], {'arg5': 5, 'arg7': 23})

Note the absence of parentheses after someFunction.

So if I understand correctly we have:

currentUser=system.security.getUsername()

import system.util
import time
import system.security

print(currentUser)

def switchUname(uname, pword):
	system.security.switchUser(uname, pword)
try:
	if currentUser != "uname":
		try:
			time.sleep(5)
			system.util.invokeLater(switchUname, ["uname", "pword"],{})
		except Exception as e:
			system.util.getLogger("AutoLogOut").warn("Error in invokeLater: " + str(e))
except Exception as e:
	system.util.getLogger("AutoLogOut").warn("Error in invokeLater: " + str(e))

Unfortunately, this has no effect on the program. It no longer throws an error, but it also doesn't actually swap the user as it did before.

invokeLater cannot pass arguments like invokeAsynchronous. You must use closures or default args in a temporary function, or use partial().

Other notes:

  • Your indentation seems borked. (Maybe?)

  • Any sleep needs to be inside a background task. Sleep must never be used directly in a Vision event, nor in an invokeLater function.

  • Your error checking needs to also be inside the delegated function.

Please go find later.py, look at its code, and examples, and examples of invoke* all over this forum.