Scripts ignore variables declared in 'module' scope

#4

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.

0 Likes

#5

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:

# 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.

0 Likes

#6

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.

0 Likes

#7

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.

0 Likes

#8

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…

# 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 :frowning:

0 Likes

#9

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

0 Likes

#10

Notice that closures/inner functions work correctly:

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…

# 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)
0 Likes

#11

Any news on this?

Thanks.

0 Likes

#12

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

0 Likes

#13

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.

0 Likes

#14

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.

0 Likes

#15

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

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.

0 Likes

#16

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.

0 Likes

#17

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.

0 Likes

#18

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.

0 Likes

#19

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.

0 Likes

#20

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.

0 Likes

#21

That all sounds fantastic. The shared section will also be very useful.

Carl, would it be possible to make it so that we can create packages at the root level from within Ignition, rather than having to create an SDK Module and import them externally?

What I mean is being able to just do ‘import dc’, rather than putting it under app and doing ‘from app import dc’. Being able to put code in a package would also be very helpful (using some sort of init system- possibly with a different name unless it works the same as normal Python).

Thanks very much.

0 Likes

#22

Possibly. I’ll keep it in mind.

0 Likes

#23

[quote=“Carl.Gould”]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.[/quote]

:smiley: :prayer: :thumb_left: :thumb_right:

0 Likes