# Scripts ignore variables declared in 'module' scope

**URL:** <https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992>\
**Category:** Ignition\
**Created:** [August 13, 2012, 3:25pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992 "2012-08-13T15:25:23Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![systemparadox](https://avatars.discourse-cdn.com/v4/letter/s/b5e925/32.png) [@systemparadox](https://forum.inductiveautomation.com/u/systemparadox)\
**Post date:** [August 13, 2012, 3:25pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/1 "2012-08-13T15:25:23Z")

</div>

Say I have the following code:

```auto
# test.py
def foo():
    return 'foo1' + bar()

def bar():
    return 'bar1'
```

This works correctly in pure Python (tested on 2.7.1):

```auto
$ python
>>> import test
>>> test.foo()
'foo1bar1'
```

However, putting this into Ignition as a script module does not work:

```auto
from app import test
test.foo() # NameError: global name 'bar' is not defined
```

Some existing posts show similar behaviour, and I am confused by the responses:

- “This has to do with how Python’s scoping rules work… You’re importing fpmi, but your def defines a new scope. The newly defined function doesn’t share scope with its declaring module.” ([viewtopic.php?p=3984#p3984](http://www.inductiveautomation.com/forum/viewtopic.php?p=3984#p3984))
- “This is just a quirk of Python scoping rules. Your logic is sound, but your **init** function doesn’t actually have “MyClass” in its scope” ([viewtopic.php?p=9135#p9135](http://www.inductiveautomation.com/forum/viewtopic.php?p=9135#p9135))
- “Python only ever has a local and global namespace” ([viewtopic.php?p=7171#p7171](http://www.inductiveautomation.com/forum/viewtopic.php?p=7171#p7171))

Looking elsewhere in the ‘pure’ Python world (as opposed to Jython and Ignition) gives a very different impression. All the documentation I can find states that Python has a module level scope. If a variable does not exist within the local scope, it should then search up the hierarchy, including the ‘module’ (or ‘file’) that the function was defined in. My own experience with Python confirms this.

Looking back, it would appear that Python has had module scope since at least before 2.1:  
[docs.python.org/dev/whatsnew/2.1 … ted-scopes](http://docs.python.org/dev/whatsnew/2.1.html#pep-227-nested-scopes)

It’s possible that this behaviour is caused by Jython, but it seems more likely that it’s down to the way Ignition’s Script Modules are loaded.

I would like to avoid having to add explicit imports to every function if at all possible.

I’m still somewhat unclear on how the script modules are loaded, which is making it hard to find a workaround. Any more information on this issue would be much appreciated.

Thanks.

---

<div class="post-metadata">

**Author:** ![systemparadox](https://avatars.discourse-cdn.com/v4/letter/s/b5e925/32.png) [@systemparadox](https://forum.inductiveautomation.com/u/systemparadox)\
**Post date:** [August 13, 2012, 4:11pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/2 "2012-08-13T16:11:22Z")

</div>

To further clarify, here is an even simpler example:

```auto
module_var = 'hello'

def test():
    print module_var # NameError: global name 'module_var' is not defined

print module_var
```

The module\_var is correctly printed when the module is first imported, but when test() runs later it does not search the module scope and so fails to find module\_var. This example works correctly in ‘pure’ Python.

Most interestingly, saving the above example as /tmp/test.py and running the following in the script playground also works:

```auto
import sys
path = '/tmp'
if path not in sys.path:
	sys.path.append(path)
import test
test.test()
```

---

<div class="post-metadata">

**Author:** ![Mdunnick](https://avatars.discourse-cdn.com/v4/letter/m/34f0e0/32.png) [@Mdunnick](https://forum.inductiveautomation.com/u/Mdunnick)\
**Post date:** [August 13, 2012, 4:26pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/3 "2012-08-13T16:26:29Z")

</div>

From what I know:

Regular python import is done as:

```auto
from module import function
```

then in the code write:

```auto
function()
```

OR

```auto
import module 
```

then in the script write:

```auto
module.function()
```

in the ignition software however you have modules in modules. So thinking about it in that way you are pretty much doing:

```auto
from module import module
```

when you do:

```auto
from app import test.
```

using the import line, not sure what you would do for a workaround, maybe from app.test import foo, but I know in the actual script writing the following should work.

```auto
print app.test.foo() 
```

---

<div class="post-metadata">

**Author:** ![dave.fogle](https://avatars.discourse-cdn.com/v4/letter/d/43a26b/32.png) [@dave.fogle](https://forum.inductiveautomation.com/u/dave.fogle)\
**Post date:** [August 13, 2012, 6:10pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/4 "2012-08-13T18:10:34Z")

</div>

When Ignition was upgraded to include the newer version of Jython there were a few things that changed and one of them happened to do with this scoping issue. You have to include app in each of your function definitions in order to call another function that was defined in the same module. So if you just add `import app` to all of your functions then everything should work as expected.

---

<div class="post-metadata">

**Author:** ![systemparadox](https://avatars.discourse-cdn.com/v4/letter/s/b5e925/32.png) [@systemparadox](https://forum.inductiveautomation.com/u/systemparadox)\
**Post date:** [August 14, 2012, 9:10am UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/5 "2012-08-14T09:10:23Z")

</div>

Mdunnick, that’s not the issue here. This is about variable scope. It only concerns imports as far as “from foo.bar import baz” creates a variable ‘baz’ (which is a ‘module’ object) in the current scope. The problem is that the module-level scope is not visible to the function like it should be.

Dave, I am assuming you mean that I have to use the full path all the time like this:

```auto
# test.py
function foo():
    import app
    app.test.bar()

function bar():
    print 'bar1'
```

This is something that I really want to avoid.

Do you have any links to more info on Jython’s part in this please? The results of my previous test imply that Jython is working correctly, and that the issue is specific to the way Script Module system works.

Thanks.

---

<div class="post-metadata">

**Author:** ![dave.fogle](https://avatars.discourse-cdn.com/v4/letter/d/43a26b/32.png) [@dave.fogle](https://forum.inductiveautomation.com/u/dave.fogle)\
**Post date:** [August 14, 2012, 4:24pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/6 "2012-08-14T16:24:09Z")

</div>

Unfortunately as of right now I don't have any further information about this issue. I have made a ticket about this issue. For now you will have to import app and then call the function as you described above.

---

<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:** [August 14, 2012, 4:28pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/7 "2012-08-14T16:28:03Z")

</div>

It’s been a while since I looked at the scripting code, particularly the script module system which has remained untouched since the beginning of time, but I suspect the root of the issue is that Ignition script modules aren’t real python modules, just a feature-incomplete facsimile that lets users organize their scripts a bit.

---

<div class="post-metadata">

**Author:** ![systemparadox](https://avatars.discourse-cdn.com/v4/letter/s/b5e925/32.png) [@systemparadox](https://forum.inductiveautomation.com/u/systemparadox)\
**Post date:** [October 30, 2012, 12:33pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/8 "2012-10-30T12:33:53Z")

</div>

This also makes class inheritance very clumsy, as you have to import the full path to the superclass every time you want to do method chaining…

```auto
# app.foo.bar
class Parent(object):
    def __init__ (self, name):
        pass

class Child(Parent):
    def __init__ (self, name):
        import app
        super(app.foo.bar.Parent, self). __init__ (name)
        # should just be:
        super(Parent, self). __init__ (name)
```

With long names and paths this becomes unreadable very quickly, not to mention all the extra import lines in every single method ☹

---

<div class="post-metadata">

**Author:** ![Michael.Stofan](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/michael.stofan/32/3342_2.png) [@Michael.Stofan](https://forum.inductiveautomation.com/u/Michael.Stofan)\
**Post date:** [October 30, 2012, 6:07pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/9 "2012-10-30T18:07:44Z")

</div>

I’ve created a bug ticket for this issue. Hopefully in the future you won’t have to do this anymore.

---

<div class="post-metadata">

**Author:** ![systemparadox](https://avatars.discourse-cdn.com/v4/letter/s/b5e925/32.png) [@systemparadox](https://forum.inductiveautomation.com/u/systemparadox)\
**Post date:** [November 29, 2012, 1:01pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/10 "2012-11-29T13:01:34Z")

</div>

Notice that closures/inner functions work correctly:

```auto
def test():
	value = 'inner works'
	def inner():
		print value
	inner()
```

As soon as another level of scope is introduced everything works correctly, it’s just the outer module scope that doesn’t.

Incidentally, this is why all the examples for invokeLater pass in event=event, which is completely unnecessary…

```auto
# Currently this:
def requestFocus(event=event):
	import system
	system.gui.getParentWindow(event).getComponent('foo').requestFocusInWindow()
system.util.invokeLater(requestFocus)

# The following version works if you wrap the event in a function, but it's supposed to work without:
def handler(event):
	def requestFocus():
		system.gui.getParentWindow(event).getComponent('foo').requestFocusInWindow()
	system.util.invokeLater(requestFocus)
handler(event)
```

---

<div class="post-metadata">

**Author:** ![systemparadox](https://avatars.discourse-cdn.com/v4/letter/s/b5e925/32.png) [@systemparadox](https://forum.inductiveautomation.com/u/systemparadox)\
**Post date:** [August 23, 2013, 3:52pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/11 "2013-08-23T15:52:48Z")

</div>

Any news on this?

Thanks.

---

<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:** [August 23, 2013, 6:47pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/12 "2013-08-23T18:47:51Z")

</div>

There’s still a ticket for this but to be honest it’s really low priority.

---

<div class="post-metadata">

**Author:** ![nmudge](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nmudge/32/3394_2.png) [@nmudge](https://forum.inductiveautomation.com/u/nmudge)\
**Post date:** [August 26, 2013, 4:03pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/13 "2013-08-26T16:03:57Z")

</div>

I think systemparadox has a really good point here.

I verified that Ignition’s modules do not follow the scoping rules of Jython 2.5 modules.

Specifically, variables, classes and functions defined or imported in module scope cannot be seen in function definitions or method definitions without reimporting them. This is in contrast to how Python and Jython work.

I have personally gotten around this problem by making global variables, which is generally something I don’t want to do.

It would be really nice if Ignition modules worked like standard Jython modules not only to be consistent and compatible with Jython/Python but also because it makes writing classes and functions and using variables in modules simpler, easier and not awkward.

---

<div class="post-metadata">

**Author:** ![systemparadox](https://avatars.discourse-cdn.com/v4/letter/s/b5e925/32.png) [@systemparadox](https://forum.inductiveautomation.com/u/systemparadox)\
**Post date:** [August 27, 2013, 9:19am UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/14 "2013-08-27T09:19:14Z")

</div>

nmudge, would you mind posting a little bit more information about how you setup global variables to work around this? I tried briefly, but couldn’t get them to initialise consistently. While this doesn’t fix all of the issues, it would at least save having to import app and system everywhere.

I appreciate that fixing this could be fairly involved, but I really do think this needs to be taken a bit more seriously. It’s a pretty fundamental issue that causes confusion for even the most basic scripts and becomes progressively worse from there.

Thanks.

---

<div class="post-metadata">

**Author:** ![nmudge](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nmudge/32/3394_2.png) [@nmudge](https://forum.inductiveautomation.com/u/nmudge)\
**Post date:** [August 27, 2013, 3:05pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/15 "2013-08-27T15:05:19Z")

</div>

Sure. At the top of every Ignition script module I put these as the first two lines:

```auto
global app,system
import app,system
```

This allows me to use app and system everywhere in a module without having to import them again. This has always worked well for me.

It is possible to declare more global variables and then import or define more things that become global. Most often I only declare system and app global because everybody knows that global variables suck.

---

<div class="post-metadata">

**Author:** ![nmudge](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nmudge/32/3394_2.png) [@nmudge](https://forum.inductiveautomation.com/u/nmudge)\
**Post date:** [August 27, 2013, 4:22pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/16 "2013-08-27T16:22:11Z")

</div>

I asked Carl, one of the Directors of Software Engineering at Inductive Automation if adding Jython/Python module scope to Ignition script modules is something that I could help with.

He said yes and gave me direction on getting started with it. I plan to start working on it soon on a part-time basis.

I work for Inductive Automation Design Services.

---

<div class="post-metadata">

**Author:** ![systemparadox](https://avatars.discourse-cdn.com/v4/letter/s/b5e925/32.png) [@systemparadox](https://forum.inductiveautomation.com/u/systemparadox)\
**Post date:** [August 29, 2013, 8:14am UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/17 "2013-08-29T08:14:02Z")

</div>

Thanks nmudge.

As far as I can tell so far, it shouldn’t be too difficult a change to make. I just think it’s passing the wrong objects to globals() and locals() and/or not saving them correctly.

---

<div class="post-metadata">

**Author:** ![systemparadox](https://avatars.discourse-cdn.com/v4/letter/s/b5e925/32.png) [@systemparadox](https://forum.inductiveautomation.com/u/systemparadox)\
**Post date:** [August 29, 2013, 9:38am UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/18 "2013-08-29T09:38:59Z")

</div>

Ah that makes more sense now. Global imports work well for app and system. The issue I had was to do with circular dependencies in our helper package, but that should work too if I can resolve that. The side-effect of having to import inside every function is that it makes everything lazy-loaded by default.

Thanks.

---

<div class="post-metadata">

**Author:** ![Carl.Gould](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/carl.gould/32/7262_2.png) [@Carl.Gould](https://forum.inductiveautomation.com/u/Carl.Gould)\
**Post date:** [August 30, 2013, 8:42pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/19 "2013-08-30T20:42:34Z")

</div>

We hope to address this in 7.7

What we’ll probably do is start phasing out the existing “app” scripts in favor of a new style of script modules that will have correct scoping and whose scripts will be individually locked. We also hope to introduce a “shared” script environment that would be global across all projects.

---

<div class="post-metadata">

**Author:** ![nmudge](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nmudge/32/3394_2.png) [@nmudge](https://forum.inductiveautomation.com/u/nmudge)\
**Post date:** [August 30, 2013, 8:49pm UTC](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992/20 "2013-08-30T20:49:30Z")

</div>

By “scripts will be individually locked” Carl means that multiple people will be able to edit different modules in the Script Module Editor at the same time.

Currently the entire Script Module Editor is locked so that only a single person can use it for editing at a time.

This doesn’t have to do with module scoping but is just additional information about plans for jython modules.

[Next page](https://forum.inductiveautomation.com/t/scripts-ignore-variables-declared-in-module-scope/4992.md?page=2)
