# Scoping in scripts

**URL:** <https://forum.inductiveautomation.com/t/scoping-in-scripts/35136>\
**Category:** Ignition\
**Tags:** ignition79, scripting\
**Created:** [May 28, 2020, 7:59pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136 "2020-05-28T19:59:53Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![john.furby](https://avatars.discourse-cdn.com/v4/letter/j/e9bcb4/32.png) [@john.furby](https://forum.inductiveautomation.com/u/john.furby)\
**Post date:** [May 28, 2020, 7:59pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/1 "2020-05-28T19:59:53Z")

</div>

I’m driving myself crazy trying to determine why/how Ignition doesn’t follow Python scoping.

I have a variable defined that is not accessible by the functions. I have to pass it explicitly somehow.

I don’t understand why I have to set `scheduledEvents=scheduledEvents` and such. I should be able to read from and write to a globally scoped variable, no? The `def` is also in the `for` loop because it wouldn’t work outside of it. I’m not a python expert but the python tutorials seem to act like I shouldn’t have to reference global variables in the call at all. Isn’t that the point of being global?

In the example, newCnt is always prints 1 but is 0 at the end.

Example:

```auto
scheduledEvents = dataset[]
db = dataset2[]
newCnt = 0

for rowNum in range(db.getRowCount()):
   def addToSchedule(eventDict, scheduledEvents = scheduledEvents, updateCnt = updateCnt,newCnt=newCnt ):
          #do schedule stuff
          newCnt += 1
          print newCnt
          return scheduledEvents
   scheduledEvents = addToSchedule(eventDict,scheduledEvents)

```

Please advise me on what I am missing.

Thank you,  
John  
`

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [May 28, 2020, 8:04pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/2 "2020-05-28T20:04:21Z")

</div>

Where are you writing this script?

Unfortunately a notion of “legacy” scoping vs “standard” scoping developed over time. Which the script uses depends where it is.

---

<div class="post-metadata">

**Author:** ![john.furby](https://avatars.discourse-cdn.com/v4/letter/j/e9bcb4/32.png) [@john.furby](https://forum.inductiveautomation.com/u/john.furby)\
**Post date:** [May 28, 2020, 9:21pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/3 "2020-05-28T21:21:06Z")

</div>

On a scripting button.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 28, 2020, 9:22pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/4 "2020-05-28T21:22:08Z")

</div>

I remember now, to edit closure variables (terminology?), you need to declare them with `global` in the function. Otherwise, it treats them as local-scoped variables.

Consider the following:

```auto
counter = 0

def addCounter():
	global counter
	counter += 1
	print counter

addCounter()
addCounter()
addCounter()

```

This will print:  
`>>1`  
`>>2`  
`>>3`

[https://www.programiz.com/python-programming/global-local-nonlocal-variables](https://www.programiz.com/python-programming/global-local-nonlocal-variables)

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [May 28, 2020, 9:41pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/5 "2020-05-28T21:41:30Z")

</div>

Do you…actually need to define `addToSchedule` at all?

If you just _do_ the schedule manipulation directly in the block of the for loop, there’s zero confusion about closures.

---

<div class="post-metadata">

**Author:** ![john.furby](https://avatars.discourse-cdn.com/v4/letter/j/e9bcb4/32.png) [@john.furby](https://forum.inductiveautomation.com/u/john.furby)\
**Post date:** [May 28, 2020, 10:45pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/6 "2020-05-28T22:45:13Z")

</div>

The loop could do one or two schedule changes depending on what the input has, so I thought it would be better to make it a def instead of writing the code twice.  
And this is also about trying to finally understand how the scoping works since I’ve run into this a bit now and have just been avoiding the issue by explicitly calling it

---

<div class="post-metadata">

**Author:** ![john.furby](https://avatars.discourse-cdn.com/v4/letter/j/e9bcb4/32.png) [@john.furby](https://forum.inductiveautomation.com/u/john.furby)\
**Post date:** [May 28, 2020, 10:49pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/7 "2020-05-28T22:49:43Z")

</div>

Now it is saying its not Global  
`NameError: global name 'updateCnt' is not defined`

I set updateCnt to 0 before the `for` loop starts.  
Here’s the full function just in case I lied to you guys about something.

```auto
def addToSchedule(eventDict, scheduledEvents = scheduledEvents ):
	global updateCnt
	global newCnt
	#check for duplicates
	if eventDict["EventID"] in existingEvents:
		seRow = existingEvents.index(eventDict["EventID"])
		#if its already in, then just update it
		print "\t\tExisting event",eventDict["EventID"]," row ", seRow
		#if updating, get current percent complete
		eventDict["PctDone"] = scheduledEvents.getValueAt(seRow,'PctDone')
		db = system.dataset.updateRow(scheduledEvents,seRow,eventDict) 
		scheduledEvents = db
		updateCnt += 1
		print updateCnt
		#set back to 0 for next event
		eventDict["PctDone"] = 0
	else:
		#if its not, then add it
		print "\t\tadding new event",eventDict["EventID"]
		schEvent = [eventDict["EventID"],eventDict["ItemID"],eventDict["StartDate"],eventDict["EndDate"],eventDict["Label"],
					eventDict["Foreground"],eventDict["Background"],eventDict["LeadTime"],eventDict["LeadColor"],eventDict["PctDone"],
					eventDict["Item"],eventDict["Qty"],eventDict["Hrs"],eventDict["Push"],eventDict["CIP"],eventDict["GPH"],eventDict["CPH"],eventDict["WM Needed"],eventDict["Protein"]]
		#[v for v in eventDict.values()]
		print '\t\tscheduledEvents',scheduledEvents
		db = system.dataset.addRow(scheduledEvents,schEvent)
		print ""
		print '\t\tdb',db
		
		scheduledEvents = db
		newCnt += 1
		print newCnt
	return scheduledEvents
#end def

```

\*edit on this, its a bit obnoxious that an UpdateRow requires a dictionary but an add row requires a list. Is there a better way to accomplish an Update or Add with the same data?

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 28, 2020, 10:52pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/8 "2020-05-28T22:52:39Z")

</div>

have you defined `updateCnt` above the `def addToSchedule(...)` function declaration?

---

<div class="post-metadata">

**Author:** ![john.furby](https://avatars.discourse-cdn.com/v4/letter/j/e9bcb4/32.png) [@john.furby](https://forum.inductiveautomation.com/u/john.furby)\
**Post date:** [May 28, 2020, 10:53pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/9 "2020-05-28T22:53:30Z")

</div>

````auto
errorCnt = 0
lineCnt = 0
updateCnt = 0
newCnt = 0
		
print 'Starting import on ',db.getRowCount(),'rows'
for rowNum in range(db.getRowCount()):```
````

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 28, 2020, 10:56pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/10 "2020-05-28T22:56:04Z")

</div>

Can you paste the whole code? My brain hurts jumping up and down trying to piece it all together

---

<div class="post-metadata">

**Author:** ![john.furby](https://avatars.discourse-cdn.com/v4/letter/j/e9bcb4/32.png) [@john.furby](https://forum.inductiveautomation.com/u/john.furby)\
**Post date:** [May 28, 2020, 10:58pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/11 "2020-05-28T22:58:42Z")

</div>

Sorry, there’s just a lot of unnecessary stuff in between I was trying to save you from.

```auto
lineCnt = 0
updateCnt = 0
newCnt = 0

print 'Starting import on ',db.getRowCount(),'rows'
for rowNum in range(db.getRowCount()):
	schEvent = []
	schDT = []
	
	cell1 = db.getValueAt(rowNum,"Line # ").strip()
	#print rowNum
	#print repr(cell1)

	if cell1!=u'':	
		#print 'Not blank'
		if cell1.find('Line') > -1:
			#LineNumCol = db.getValueAt(rowNum,"Line # ")
			LineNum = ""
			for char in cell1[cell1.find('Line')+5:]:
				if char.isnumeric():
					LineNum += char
				elif char.isspace() and LineNum == "":
					continue
				else:
					print 'Line', LineNum
					break
		else:
			LineNum = ""
	
	if LineNum != "" :
		#print 'Line',LineNum,"-" + str(db.getValueAt(rowNum,"Process Order"))+"-",db.getValueAt(rowNum,"Process Order").isnumeric()
		if (db.getValueAt(rowNum,"Process Order").replace(".0","")).isnumeric():
# while currEventsInd < currEventsRowCount:
# print currEvents.getValueAt(currEventsInd, "EventID"), "=",db.getValueAt(rowNum,"Process Order")
# if currEvents.getValueAt(currEventsInd, "EventID") == db.getValueAt(rowNum,"Process Order"):
# break
# currEventsInd += 1				
			
			try:
				desc = str(db.getValueAt(rowNum,"Description ")).title()
				shortDesc = desc
				print '\t',shortDesc
				
				def addToSchedule(eventDict, scheduledEvents = scheduledEvents ):
					global updateCnt
					global newCnt
					#check for duplicates
					if eventDict["EventID"] in existingEvents:
						seRow = existingEvents.index(eventDict["EventID"])
						#if its already in, then just update it
						print "\t\tExisting event",eventDict["EventID"]," row ", seRow
						#if updating, get current percent complete
						eventDict["PctDone"] = scheduledEvents.getValueAt(seRow,'PctDone')
						db = system.dataset.updateRow(scheduledEvents,seRow,eventDict) 
						scheduledEvents = db
						updateCnt += 1
						print updateCnt
						#set back to 0 for next event
						eventDict["PctDone"] = 0
					else:
						#if its not, then add it
						print "\t\tadding new event",eventDict["EventID"]
						schEvent = [eventDict["EventID"],eventDict["ItemID"],eventDict["StartDate"],eventDict["EndDate"],eventDict["Label"],
									eventDict["Foreground"],eventDict["Background"],eventDict["LeadTime"],eventDict["LeadColor"],eventDict["PctDone"],
									eventDict["Item"],eventDict["Qty"],eventDict["Hrs"],eventDict["Push"],eventDict["CIP"],eventDict["GPH"],eventDict["CPH"],eventDict["WM Needed"],eventDict["Protein"]]
						#[v for v in eventDict.values()]
						print '\t\tscheduledEvents',scheduledEvents
						db = system.dataset.addRow(scheduledEvents,schEvent)
						print ""
						print '\t\tdb',db
						
						scheduledEvents = db
						newCnt += 1
						print newCnt
					return scheduledEvents
				#end def

				eventDict["EventID"] = db.getValueAt(rowNum,"Process Order")
				eventDict["ItemID"] = LineNum
				#print '\tStart',db.getValueAt(rowNum,"Start ")
				eventDict["StartDate"] = system.date.parse(db.getValueAt(rowNum,"Start "),'M/dd/yy HH:mm')
				eventDict["EndDate"] = system.date.parse(db.getValueAt(rowNum,"End "),'M/dd/yy HH:mm')
				eventDict["Label"] = shortDesc

				eventDict["Background"] = "color(214,255,198,255)"#Production light green

				eventDict["Item"] = db.getValueAt(rowNum,"Item")
				eventDict["Qty"] = db.getValueAt(rowNum,"Qty ")
				eventDict["GPH"] = db.getValueAt(rowNum,"GPH")
				eventDict["CPH"] = db.getValueAt(rowNum,"CPH")
				eventDict["WM Needed"] = db.getValueAt(rowNum,"WM Needed")
				eventDict["Protein"] = db.getValueAt(rowNum,"Protein")
				eventDict["Hrs"] = db.getValueAt(rowNum,"Hrs. ")
				eventDict["Push"] = db.getValueAt(rowNum,"Push ")
				eventDict["CIP"] = db.getValueAt(rowNum,"CIP ")
				
				scheduledEvents = addToSchedule(eventDict,scheduledEvents)
				print '\tscheduledEvents',scheduledEvents
				
					
				if db.getValueAt(rowNum,"CIP ") != '':
					print '\tadding CIP'
					time = db.getValueAt(rowNum,"CIP ").split(":")
					hour = int(time[0])
					min = int(time[1])
					EndTime = system.date.addHours(system.date.addMinutes(system.date.parse(db.getValueAt(rowNum,"End "),'M/dd/yy HH:mm'),min-10),hour)
					
					eventDict["EventID"] = eventDict["EventID"] + " CIP"
					eventDict["StartDate"] = system.date.addMinutes(system.date.parse(db.getValueAt(rowNum,"End "),'M/dd/yy HH:mm'),5)
					eventDict["EndDate"] = EndTime
					eventDict["Label"] = "CIP"
					
					eventDict["Background"] = "color(107,151,232,135)"#CIP Dark Blue
					
					eventDict["Item"] = ""
					eventDict["Qty"] = ""
					eventDict["Hrs"] = ""
					eventDict["Push"] = ""
					eventDict["CIP"] = ""
					
					scheduledEvents = addToSchedule(eventDict,scheduledEvents)
					print '\tadding CIP'
					print '\tscheduledEvents',scheduledEvents
					
			except:
				errorCnt += 1
				print 'had error on ' + str(LineNum) + '\n' + str(traceback.format_exc())
				#logger.error(str(traceback.format_exc()))
			lineCnt += 1
```

---

<div class="post-metadata">

**Author:** ![john.furby](https://avatars.discourse-cdn.com/v4/letter/j/e9bcb4/32.png) [@john.furby](https://forum.inductiveautomation.com/u/john.furby)\
**Post date:** [May 28, 2020, 11:08pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/12 "2020-05-28T23:08:05Z")

</div>

It’s got to have something to do with the `for` loop or the fact that all of that is a in a function of its own since I am using system.util.invokeAsynchronous and system.util.invokeLater

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 28, 2020, 11:18pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/13 "2020-05-28T23:18:20Z")

</div>

Ah, that’s where the issue is, in calling it using invokeLater/Async. It works if called directly. This is one for @paul-griffith

EDIT:  
This works. You just need to define your variables in the same scope as you call the async/invokeLater function. To use it without declaring in this scope, you would need to use `nonlocal` instead of `global`, however Python 2.x doesn’t support `nonlocal`

```auto
updateCnt = 0

def h():
	def add():
		global updateCnt
		updateCnt += 1
		print updateCnt
		
	for i in range(2):
		add()
system.util.invokeLater(h)

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [May 28, 2020, 11:29pm UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/14 "2020-05-28T23:29:36Z")

</div>

Hmm, so the _entire_ snippet you posted is inside a function definition that’s being `invokeAsync`ed? Best guess is that invokeAsync is doing something weird, and ends up giving you Python \<=2.5 scoping rules, instead of Python 2.7 scoping rules (which are basically the same as Python 3).

Either way, scoping in general with invokeAsync is distinctly non-intuitive.

---

<div class="post-metadata">

**Author:** ![john.furby](https://avatars.discourse-cdn.com/v4/letter/j/e9bcb4/32.png) [@john.furby](https://forum.inductiveautomation.com/u/john.furby)\
**Post date:** [May 29, 2020, 12:14am UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/15 "2020-05-29T00:14:34Z")

</div>

So, basically, everything is screwed up and annoying because I am doing invokeAsync?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [May 29, 2020, 12:16am UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/16 "2020-05-29T00:16:38Z")

</div>

Try moving your whole block of code - everything you’re `invokeAsync`ing, into a project scoped library. That may clear up the scoping issues.

---

<div class="post-metadata">

**Author:** ![john.furby](https://avatars.discourse-cdn.com/v4/letter/j/e9bcb4/32.png) [@john.furby](https://forum.inductiveautomation.com/u/john.furby)\
**Post date:** [May 29, 2020, 12:30am UTC](https://forum.inductiveautomation.com/t/scoping-in-scripts/35136/17 "2020-05-29T00:30:26Z")

</div>

I will try that out first thing tomorrow. That sounds like a great idea to simplify all of this. It was a lot of a code to put on a button anyway.
