Scripted objects migration

Introduction
Scripted objects are rebuilt every time a FCStd document is opened. To do this the document keeps a reference to the module and Python class that were used to create the object, along with its properties.

Particularly focus on this part:

If the module or class is no longer present, the object will fail to load correctly. This means that once an object is created using a particular class, the module should no longer be moved or renamed because if this is done, previously saved objects will break.

However, a valid reason for moving or renaming the class is to improve the structure and maintainability of the original code, for example, when restructuring an entire workbench. In this case there are various strategies to migrate old objects to using a new class. This is done in order to retain backwards compatibility, when outright breaking of old documents must be avoided.

Old object
An old object is defined in a module which is at the root of the workbench.

An object can be created using this class, and it can be saved to. If no particular viewprovider is assigned to the new object, its proxy class is simply set to a value different from, in this case, to.

Python console session with the basic properties omitted.

New object
Now we consider that the workbench is restructured, so that classes aren't just at the root directory, but instead are inside an directory. Complex workbenches that have many different types of objects should be structured in directories including objects, viewproviders, Gui Commands, task panel interfaces, etc.

This new class will refer to the same type of object, but both the module name as well as the class name have been renamed. Moreover, the properties also have changed; one property has been renamed, and a completely new property has been added.

If we create a new object with this new module we will have the following console session.

Method 1. Migration by redirecting the class
We will migrate the older object by redirecting the old class. The original class is deleted, and the name of the class is simply redirected to point to the new class.

Any document that tries to load will be redirected to load  instead.

If we open the document, and inspect the properties of the object in the Python console we will see that the older properties are conserved, but the object has a new Proxy class.

However, in this case we don't see the new properties of the new class. The reason is simply that the older object didn't have these properties. When was redirected to, only the proxy class changed, but previous information was retained.

Now, if the document is saved and opened again, it will automatically look for, and it will not require any more. The file may be removed permanently from the system as long as all older objects have been migrated to the new module. If the old module is removed but an object hasn't been migrated, the report view will show a message like this when opening a document containing such object.

If it is not realistically possible to migrate all older objects, say, because the old module was used in a workbench for many years, then must be kept as long as it's deemed necessary to give users the opportunity to migrate their objects.

Advantages and disadvantages
Advantages


 * Edit_OK.svg This is the simplest method that just requires redirecting an old class to a new class.
 * Edit_OK.svg Old properties are conserved as long as the new class doesn't override them.
 * Edit_OK.svg This is good if the old class and the new class have the same properties (handle the same type of data) but only their module or class name is different.

Disadvantages


 * Edit_Cancel.svg The new class keeps the old properties of the object, which is not always desired.
 * Edit_Cancel.svg New properties or renamed properties aren't handled, so the object will load but it may not show the correct behavior of the new class.
 * Edit_Cancel.svg The old module may have to be kept indefinitely to migrate all old objects created in the past.

Method 2. Migration when restoring the document
We will migrate the older object by modifying the old class. The majority of the original class is deleted, and instead the method is implemented. When this method exists, it will run when the document tries to restore an object that uses the class, so this is the opportunity that we have to assign a new class, manipulate the information, or print messages.

In this case, we assume that we have also defined a new viewprovider in the module. If we don't want to migrate this class, we may omit everything after the check.

A more complex example checks first that the proxy class is of the type that we are looking for, and only proceeds with the migration if it's the right type.

Assuming that we already changed the old module in this way, if we open a document with an old object, we will see the messages mentioning the use of the new classes.

Inspecting the object from the Python console we will see that the older properties are conserved, and in addition, new properties were added together with the new Proxy class.

The old properties were and ; the new properties are, , and. The migrated object retains the original two properties, and gains three new properties. However, since the new has the same name as the older property, the new property is renamed with an incremental number. Presumably this is not what we want.

Given that the classes are meant to handle the same type of object, we would like a migration in which transforms into, and  is simply assigned to the new , and there are no duplicate properties.

Advantages and disadvantages
Advantages


 * Edit_OK.svg This method allows us to check that the class that we are migrating is the right class, instead of simply redirecting to a newer class.
 * Edit_OK.svg Similar to method 1, old properties are kept as long as the new class doesn't override them.
 * Edit_OK.svg Unlike method 1, new properties are always added, however if they have the same name, they will be renamed.
 * Edit_OK.svg The migration is not immediate, we can still manipulate the information, or print messages while the object loads.

Disadvantages


 * Edit_Cancel.svg It is more verbose than method 1 because we need to implement the method to migrate the object.
 * Edit_Cancel.svg It always adds the new properties, so it may create duplicated properties in case the new properties have the same name as the old properties. This has to be handled manually.

Method 3. Migration when restoring the document, manually handling the properties
This is an extension of method 2. In the method we need to save the values of the properties that we want, and then we can remove these original properties. This is done so that when the new class is used, it will assign the new properties without risking name collisions with the older properties.

In this example once more we assume that we are using a custom viewprovider, with at least one custom property.

We can see that the old values are stored in an auxiliary dictionary, then the old properties are removed, then we add the new class, and finally we assign the previously saved values to the new properties. In this moment we can transform the saved values as necessary for the new class. For example, the is set to 3 times the old, and the new  simply receives the value of the old. As we know how the old and new classes are supposed to behave, we have the liberty of manipulating the data to migrate the object as we want.

We can only remove properties that were added by Python classes when we built the scripted object. Other attributes belong to the base C++ object and can't be removed.

Assuming that we already changed the old module in this way, if we open a document with an old object, we will see the messages mentioning the use of the new classes. Inspecting the object from the Python console we see that the older properties are removed, and only the new properties exist.

Since in the old class the property didn't exist, nothing was done with it. It simply was created by the new class.

Links

 * Migrating and upgrading old scripted objects