Gateway Script global name not defined

I have recently moved a project script to a Tag Change Gateway Script so I could have it run without any clients open. But I’ve run into an issue that makes absolutely no sense “global name … not defined” and this was with globally declared functions I’ve tested the exact code within the script console and there were no issues. what do I make of this?
This is the simplified code with the error with proprietary code stripped out:

def generateReport():
	return generateReportPart()
	
def generateReportPart():
	return ''

report = generateReport()

and the error:

line 2, in generateReport NameError: global name 'generateReportPart' is not defined

Yes. This is happening because there is a bug in Ignition with Gateway scripts. Gateways scripts still have the old legacy scoping enabled by default.

The bug is this: variables defined in the outermost scope can not be see by functions. In this case you have the generateReportPart variable that holds a function and you try to execute it in the generateReport function. This is perfect legal Python – but as I said the error occurs because of the scoping bug in Gateway scripts.

I wrote about the scope change in Ignition here: http://www.perfectabstractions.com/blog/new-scripting-features-in-ignition-77-part-1#standard-scoping

The best thing to do is write all your code in a project library function and then simply execute that function in your Gateway script.

2 Likes

Dead Thread I know, but I am having the exact same issue. What do you mean by a project library function?

Define functions in a project script module. Call those functions as project.someName.functionName(). In the designer, navigate to Project => Scripts => Script Library.

Perfect!! Thank you.

I’ve got the same issue happening. I have a script in my project script library called Test:

def testScript()
    print "script executed"

If I assign the following script to a button click, the script executes:

project.Test.testScript()

Now, I create a tag UDT, and on one member of that UDT, put the same code in a tag change event script:

project.Test.testScript().

I create an instance of that UDT, and change the value of the tag with the tag change event script associated with it. The script does not execute, and the diagnostic log tells me “global name ‘project’ is not defined”.

Any ideas?

Tag Change Event Scripts are not the same thing as a Tag Event Value Change Script. That’s the point. Things attached to the tag definition or a UDT definition are outside the project system and therefore have no access to any project.* scripts.
Look at the gateway event scripts under the project’s “Scripting” section of the designer nav tree. Scripts defined there are part of the project, not the tag, and therefore have access to project script modules.

That makes sense, thanks for the explanation. My application really needs the script attached to the UDT definition, so perhaps I need to think about it differently. To avoid derailing this thread, I’ll start a new one and describe what I’m trying to achieve more fully.

I’m having a mind-boggling issue…

I have this in a gateway tag change event:

site_path = "foobar"        # redacted
logger.info(site_path)
switched_off |= set(al.replace("Site", site_path) for al in switched_off)
switched_on |= set(al.replace("Site", site_path) for al in switched_on)

I get this error:
com.inductiveautomation.ignition.common.script.JythonExecException: NameError: global name 'site_path' is not defined
But I also get the log entry with site_name’s value…

Is it something with the generator expression ?
What am I missing ?

My intuition is that you’ve reduced or redacted this script before sharing with us to the point where you’ve removed the actual problem.

Also, gateway events use legacy python scoping rules, which are weird. Move everything to a project library script function and retest.

I hope legacy scoping isn’t that weird :grimacing:

I removed things before that snippet, that compute switched_on and switched_off, and a tag write after it, but nothing in that block has been modified/removed expect for the value of site_path, which was a literal too.

I’ll try moving things in the project library, but… still… wth ?
the log line works properly, and the very next doesn’t !

Top level variables in legacy scoping are truly global, so can interact with other scripts using the same names (in parallel–multithreaded). ):

So, yes, that weird.

Putting it in a project library seems to fix the issue, but, really…
Why is the variable just fine on one line, then undefined on the next ?
That’s not just weird. Weird was fine, but we left weird a few thousand miles back.

I've just had this same issue on a scheduled gateway script that was just calling a project script (v8.1.22). It only errored once and has been fine every other time.

Is there anything I can do to make this more robust? Or maybe a way to re-run a scheduled script if it fails?

Make all your event scripts one-liners that call a project library script function.

Always.

2 Likes

I'm so glad I've stumbled onto this thread, as I was about to lose my mind.
I guess the same applies to python iterators running native in gateway event scripts?

result = system.db.runNamedQuery('Recipe/Select_UnknownMaterials', parameters)
	
unknownMaterials = list(result.getValueAt(i, y) for i in range(result.getRowCount()) for y in range(result.getColumnCount()))

Would end up with 'NameError: global name 'result' is not defined'.

What I don't understand however is why when moving the script into the project library it magically works?
But thats most likely just me failing to understand the full extend of legacy scope vs standard scope.

may I suggest

from itertools import chain

return list(chain.from_iterable(system.dataset.toPyDataSet(result)))
1 Like