Changes in Python classes are not visible in the Designer until project restart

Good morning,

We use Python classes in our projects, we strive to be OOP in order to optimize and reuse our code.

Among other things, we have a service layer between our Perspective UI and our data sources, this layer consists of classes and base classes with inheritance and in Python.

The problem we noticed is that when we are in the Designer and modify a class (new method or new parameter in a constructor/method) the new functionality or adjustment is not visible in the Designer, so we have to re-open the project to see the changes in the Designer.

Having to re-open the project is a workaround, but the question is, is it possible that when modifying a class, the Designer can see the change immediately rather than having to re-open the project each time the changing a class?

@pturmel will know why without a doubt, but in the meantime my money is on top-level classes not reloading because they're only loaded with the project. Or something like that.

As to how to fix it... I can't say for sure, but I have ideas - I'll try them in a sandbox and report.

note - you don't need OOP to optimize and reuse code.

Perspective scripts run in the gateway, even when testing from the designer. You certainly have to save the project to get any gateway scope to recognize the changes (see the scripting restart log messages). You should not have to re-open the project.

The designer script console itself should recognize local changes after commit if you refresh it.

This is not a new problem, we have had this behavior since we have implementing classes, almost 18 months of development on several projects.

Look at my screenshot, I added a to_string(self) and the designer don't see it until I reopen the project.

So if you say there is no problem on your end, I can't go further on this topic and we will continue to reopen the project because the problem is real.

Thanks :worried:

image

Did you save the project after you made that change? Did you see the scripting restart message in the gateway log for the project?

Are you using import to bring names from one project library script into another? (Don't--always use fully-qualified names of Ignition project library script objects.)

Just to make sure it's clear: Phil's not an employee of Inductive Automation and this forum isn't an official support venue. If you've been having issues for 18 months, have you contacted support?

As for the issue at hand, we actually recently implemented a fix for essentially this issue (earliest possible release 8.1.30, more likely 8.1.31). To wit:

This is always going to be true.

This will no longer be true for Perspective scripts, only within the same Designer session. So 'local' changes to project libraries can be made and tested in Perspective within the Designer.

This will still very much be true.

4 Likes

Yes I allways save the changes.

Yes I see the Restarting script in the log

I can't import because we are using a factory and we create the service (Python class) with importlib as needed.

import importlib
def factory(modulename, classname, session):
return getattr(importlib.import_module(modulename), classname)(session)

There's an import line in the script you showed:
from dch.base import ServiceBaseDch
If that's in your project library, don't import it, it's already been imported for you. Use the full name instead.
ie:

class Product(dch.base.ServiceBaseDch):
    def __init__(self, session):
        dch.base.ServiceBaseDch.__init__(self, session, "Product")

    def to_list(self):
        return None
    
    def to_string(self):
        return ""

Also I'm not sure what the init code does, but I kinda feel like it's trying to do super with super ?

super(Product, self).__init__(session, "Product")
1 Like

Pretty sure this is a problem, as this will cause importing from a project library script if the underlying type is in a project library script. Don't import from project library scripts. I think it also breaks Ignition's auto-import logic that is triggered by fully-qualified object names.

Move anything involving this kind of dynamic class generation into the site-packages hierarchy, and simply tolerate the delay for noticing changes there.

Hello,

I tried dch.base.ServiceBaseDch and it didn't work, even after restarting the project, I have to keep the 'from dch.base import ServiceBaseDch' to make it work.

I tried super() long time ago and it didn't work.

image

In @Stephane_Gagnon there is a bug that prevents super() from working.

Then importlib is not going to ever work well with Ignition. Move your importlib functionality to site-packages, where normal importing is expected.

Huh. super works fine for me.

The issue with super is only with subclassing Java classes and trying to call protected methods. A red herring here, I'm pretty sure.

1 Like

That post is from 7.9 so it may have just been the 2.5 interpreter, and it might work fine with pure Jython classes, but I have stopped using it since Paul pointed it out.

I try to stay away from objects programing as much as possible anyway.

3 Likes

The problem addressed here is that the Designer does not see the changes made in a class, not a debate on the OOP.

We are greatly reducing the amount of Python code scattered everywhere like in some of our old projects.

Using the strength of Python and OOP allows us to have efficient, structured and easy to modify projects.

We aim to have no business logic in the Perspective presentation layer, the Python layer is for consulting another API which contains all the business logic.

Thanks for all your suggestions, we will investigate more how it works behind the scene.

The goal is laudable, and I don't think anyone here disagrees with it. However, in our collective experience - heavily class-based, OOP styles just don't mesh well with Ignition, because inevitably you reach to OOP to collect state...but in Ignition, you're liable to have that state pulled out from underneath you whenever the scripting system needs to restart things.

Phil will probably have more concrete advice here, but I'd strongly recommend encapsulating state in any of three areas, depending on what it is:

  1. The local visualization system (Perspective, Vision)
  2. The tag system
  3. system.util.globals, a persistent dictionary that allows you to store data across script engine restarts. Do not put arbitrary user defined classes here, or you will leak memory. However, for storing base Python or Java standard library types, it's ideal.
2 Likes
  1. Yes

  2. Yes, as long as it isn't so large or changes so often that it stresses the internal database (where the values are persisted).

  3. Yes, with strict adherence to the no user-defined objects rule. A possible strategy:

Create your user-defined classes as wrappers around python standard dictionaries. Place those in the persistent dictionary hierarchy in a hierarchy that allows you to reconstruct the class instances from them on scripting restarts. Store all state in the inner dictionary, using python standard objects only. Define the class methods and properties to read/write into keys of that dictionary. When establishing relationships between class instances, include the information needed to look up the related objects in the persistent hierarchy.

1 Like