Python typing issues

I have been running across strange behavior with data types when passing or returning from functions. In the same script I have run across two issues since the update to 8.1.19

Summary
  1. When passing a pyDataSet to a function it becomes a basic dataset (which breaks when indexing)
		PsetPDS = system.dataset.toPyDataSet(PsetDS)


system.perspective.print(psetManagement.executeRule(PsetDS))

gives the result:
class com.inductiveautomation.ignition.gateway.datasource.BasicStreamingDataset

def executeRule(rule):
#broken for some reason won’t access the dataset like it should
#changed to Basic streaming for no reason
#added return type just for testing type
return type(rule)
#This is original code that should run but breaks due to elements not being indexable
if rule[0][“operationType”] == 0:
psetManagement.deletePADSA(rule)

  1. When using system.db.runNamedQuery, the return type is dependent on results.
eolIdx = system.db.runNamedQuery("getSnEol",{"serial":serial})

gives result of long if the query returns a row of data or a basic dataset if the serial number isn’t found

It seems like Jython is interpreting the results in a way that leads to unexpected behaviors, is there a way to enforce datatypes across functions?

def executeRule(rule):
	#broken for some reason won't access the dataset like it should
	#changed to  Basic streaming for no reason
	return type(rule)
	#This code should run but breaks due to elements not being indexable
	if rule[0]["operationType"] == 0:
		psetManagement.deletePADSA(rule)

This function looks weird. You’re comment that that part should run isn’t true since you have a return type(rule) above it (which will also never equal True) but perhaps that’s for testing.

The only thing I’ve noticed that is somewhat annoying about the datasets from query functions is that system.db.runNamedQuery do return a BasicDataset while the system.db.runQuery/runPrepQuery return PyDataSet.

However I don’t see how or why a function call would change the type. Can you prove this ? What happens if you modify your code like

PsetPDS = system.dataset.toPyDataSet(PsetDS)
print type(PsetPDS)
psetManagement.executeRule(PSetDS)

def executeRule(rule):
	#broken for some reason won't access the dataset like it should
	#changed to  Basic streaming for no reason
	print type(rule)

In designer for instance running this doesn’t show any implicit conversion of the dataset types

testDS = system.dataset.toDataSet([],[[]])
testPyDS = system.dataset.toPyDataSet(testDS)

print type(testDS)
print type(testPyDS)

def func(ds):
	print type(ds)

func(testDS)
func(testPyDS)
<type 'com.inductiveautomation.ignition.common.BasicDataset'>
<type 'com.inductiveautomation.ignition.common.script.builtin.DatasetUtilities$PyDataSet'>
<type 'com.inductiveautomation.ignition.common.BasicDataset'>
<type 'com.inductiveautomation.ignition.common.script.builtin.DatasetUtilities$PyDataSet'>

This is not something I’ve ever run into.

Sorry the function is a little weird since the formatting ate it. There are two separate bits of code and the return statement was there to try and figure out what was going wrong. The missing line is

system.perspective.print(psetManagement.executeRule(PsetDS))

I made some edits to the original post

Maybe it’s different since perspective is running on the gateway scope but I find that a bit hard to believe, I don’t know though, but I have just never run into types changing on me by themselves with Ignition.

Obviously at this point PsetPDS = system.dataset.toPyDataSet(PsetDS) its a PyDataSet, but you’re saying there is no other script in between and when its inside executeRule the type is BasicDataset? Can you show the print statement results of showing right after converting to the pydataset and the print statement inside the function? The only thing I can think is that you are doing some data manipulation in between that accepts a pydataset or a basicdataset as a argument that returns a basicdataset always.

The function is in the script library and is being called on a transform. The exact code snippet is written like this that is giving that output:

if PsetDS.getRowCount() > 0:
		PsetPDS = system.dataset.toPyDataSet(PsetDS)
		system.perspective.print(psetManagement.printPyDataSet(PsetPDS))
		system.perspective.print(PsetPDS[0]["operationType"])
		system.perspective.print(psetManagement.executeRule(PsetDS))

truncated version of the function that just gives the return type (with my comments):

def executeRule(rule):
	#system.perspective.print(rule)
	#broken for some reason won't access the dataset like it should
	#changed to streaming for no reason
	return type(rule)

There is no other code executed on the PsetDS data at this point.

You're passing the original dataset to executeRule, not the new PyDataSet.

2 Likes

Wow, right.
OK, I feel dumb about missing that. I am going to leave up part 2 because that is still unusual.
Thanks for your help though.

2 Likes

You should know that somewhere in v8.1, the PyDataset class was updated to implement the Dataset Interface, making it possible to assign a PyDataset directly into any property that accepts Datasets.

This might have been a mistake. Historically, almost every dataset encountered in Vision or in the Designer was a BasicDataset or one of its subclasses. In the Gateway, a BasicStreamingDataset was the other common implementation. The PyDataset implementation of Dataset may break long-standing assumptions in various places.

IMNSHO, the PyDataset type should be retired in favor of a universal Jython proxy for the Dataset interface, similar to the PyComponentWrapper proxy for Vision components. Such a proxy can offer the right methods for python iteration and dictionary emulation for all Dataset implementations. Would be more performant, too, I suspect.

For v8.3, please? If not sooner.

4 Likes

I read your code and I also missed it lol. PsetDS and PsetPDS are very close in naming.

1 Like