Passing a list into runScript() expression binding

Here's my test script.

def addStar(arr):
	return [x + "***" for x in arr]

Running myScripts.addStar(['brush', 'comb']) in Script Console gives me
['brush***', 'comb***']. All good so far.

I know that one can't create lists directly in expressions (see Indirect Tag History Binding Expression - #3 by PGriffith) so I've created a custom property myArr to hold the list ['brush', 'comb'].

On another tag I've created an expression binding:
runScript("myScripts.addStar", 0, {view.custom.myArr})

The Binding Preview gives the error message,

Error_ExpressionEval("Error executing script for runScript) expression:myscripts.addStar")

Can anyone give me some guidance on this?

Make a simple script to return the type of the passed parameter (it won’t be list).

Then modify your addStar script to handle the actual type

Objects are Java hashmaps, lists are Java arrays if I recall correctly.

1 Like

Thanks for the tip.

def addStar(arr):
	return type(arr)

Script console myScripts.addStar(['gh', 'fd']) returns <type 'list'> as expected.

The expression binding runScript("myScripts.addStar", 0, {view.custom.myArr}) now returns class.org.python.core.PyArray.

How do I convert that to a list / array?

JSON or GSON encode/decode.

Don’t have my snippets with me but may report back shortly with example

1 Like

Type hints are wrong (this is an old version in my clipboard history) but code usually works. Sometimes GSON can’t handle nested types. I think Phil’s module has a method to do this as well, but assume you’re not using it in this case

	@staticmethod
	def unWrap(wrappedObj): # type: (WrappedObject | Sequence[WrappedObject]) -> UnwrappedObject | Sequence[UnwrappedObject]
		"""Unwraps Objects to dict - Used for finnicky JSON encoding and message handlers
			Example: `_system.util.unWrap(self.view.params)`

		Args:
			wrappedObj: WrappedObject | Sequence[WrappedObject]

		Returns:
			dict: UnwrappedObject | Sequence[UnwrappedObject]
		"""		
		# return dict(wrappedObj) # shallow dict, sendRequest doesn't like
		# return system.util.jsonDecode(system.util.jsonEncode(wrappedObj)) # chokes (weird types/depth limitations)
		# import json
		# return json.loads(json.dumps(wrappedObj)) # # chokes (weird types/depth limitations)
		# https://forum.inductiveautomation.com/t/function-to-convert-json-string-or-python-dict-into-objectwrapper/32861/15
		from com.inductiveautomation.ignition.common import TypeUtilities
		return TypeUtilities.gsonToPy(TypeUtilities.pyToGson(wrappedObj))

Related: Curiosity: why are documented methods throwing AttributeError (is my ignorance showing?) - #14 by hunterdg

Thanks for all that effort. It all seems a bit bonkers. I'm passing a list. What happened to it on the way to the script. (And all of this is because I'm being a good boy and moving code out of expression transform scripts and into the project library.)

I had a quick go of your function (which now introduces the notion of GSON!) but couldn't clear the error.

I'm going to fix a lawnmower instead. I hope there aren't left-hand threads holding the blade on.

3 Likes

list(param) might work for your simple use case.

Perspective parameters are not python objects, they’re Java objects.

Bet you could do a parameter binding chained with a python transform that just calls the library script and passes in {value}. That might handle type conversion transparently.

return type(list(arr)) gives me class.org.python.core.PyList on the Expression Preview.

return list(arr) gives me ["brush", "comb"] as does return arr.

return [x + "***" for x in list(arr)] gives me the error again.

Thanks for sticking with me on this.

PyList is what you want so you’re making progress.

Maybe that wasn’t the problem in the first place

I bet return type(x[0]) is a QualifiedValue

Try return [x.value + "***" for x in list(arr)]

You can modify that test script with a try/except error as e; return e to see the actual error as you refine.

(Phil’s module also has an unQualify() function for precisely this purpose lol)

Bingo!
return [x.value + "***" for x in list(arr)] returns ["brush***", "comb***"].
So that was it. Thank you very much.

Meanwhile the lawnmower deck is cast aluminium and one of the engine mounting locations has cracked off. It might be repairable - but maybe not on this forum.

2 Likes

:laughing:

Might not need list() anymore.

Correct. return [x.value + "***" for x in arr] does the job. Better again.

1 Like

My bad for overcomplicating right off the bat :man_facepalming:

Cheers

Psssst!

unQualify()

I'm aware of that function but your Integration Toolkit isn't installed on the customer's gateway. I'll be surprised if they'll add it.

What if you changed your function to take an arbitrary number of values instead of a list ?

def add_stars(*args):
	return [arg + "***" for arg in args]

I don't know where your values come from, so this may not be practical for your use case, but if it is it will remove the need for a temporary list in your custom properties.

Thanks, Pascal. I was aware of that option from the docs. In my case I'd be splitting up an array from the custom prop which would make the expression more complex. Passing the Java object into the script and sorting it out there suits me better.

My point was that you might skip that custom prop array entirely, depending on how exactly you populate that array.

1 Like

I created a couple library functions for this scenario to convert lists and dictionaries of Qualified Values:

def getListOfValues(listOfQV):
	if listOfQV is None:
		return None
	return [val.value for val in listOfQV]
	
def getDictOfValues(dictOfQV):
	if dictOfQV is None:
		return None
	out = {}
	for key in dictOfQV.keys():
		out[key] = dictOfQV[key].value
	return out
1 Like

Consider instead:

	return dict([(k, qv.value) for (k, qv) in dictOfQV.items()])
2 Likes