This article describes how to work with the SystemWeaver Client API with Python in Ubuntu. Running it on other flavors of Linux should work in a similar way although it has not been tested.


Prerequisites

  • Installation of Ubuntu (testing was done with version 22.04 on a virtual machine, e.g., VirtualBox)
  • Installation of dotnet (example: sudo apt install dotnet-sdk-6.0)
  • Installation of IronPython (testing was done with Ironpython3 (using net6.0) which is an open-source implementation of the Python programming language tightly integrated with .NET. IronPython can use the .NET and Python libraries, and other .NET languages can use Python code just as easily. It can be found here https://github.com/IronLanguages/ironpython3
  • Installation of git (example: sudo apt install git)
  • Various SystemWeaver dll files which should be obtained from the SystemWeaver.Connection.Core NuGet package

Preparing IronPython and SystemWeaver Dlls

  1. To get started, download the source code of IronPython and build it. In our example, all of the code from git is placed in ironpythongit/ironpython3.
    mkdir ironpythongit
    cd ironpythongit
    git clone https://github.com/IronLanguages/ironpython3.git
  2. Next, build IronPython. You can use powershell to do this. Example: 
    Install powershell:
    sudo snap install powershell --classic
    
    Build Ironpython
    powershell
    cd ironpython3
    ./make.ps1 package
  3. You can then create paths for IronPython, or just run it from the Release folder. In the Release folder for net6.0, there is an executable called ipy which can be used to run IronPython.
  4. Copy the following SystemWeaver files to the Release folder (ironpython3/bin/Release/net6.0)
    • SystemWeaver.ClientAPI.dll
    • SystemWeaver.Common.dll
    • SystemWeaver.Connection.dll
    • DotNetZip.dll
    • NLog.dll
    • K4os.Compression.LZ4.dll
    • System.Security.Permissions.dll

The above files can be copied from the SystemWeaver.Connection.Core NuGet package*. The SystemWeaver dll files are located directly in the Nuget package. The third party dependencies can be retrieved by creating a new dotnet project and run dotnet restore. After the restoration, the dll can be located in the build output folder (bin/Release/net6.0).


Running Python Script

You are now ready to run your python script. 


Example Script

"first_test.py"

~/ironpythongit/ironpython3/bin/Release/net6.0/ipy first_test.py x04000000000014CA

Below is a Python test script. It loads the SystemWeaver client API. Indata for the script is a SystemWeaver item handle (e.g., x04000000000014CA). The script looks for some part-types and traverses down a tree.


It is important to include "import clr" in the script which makes it possible for Ironpython to interop with .Net.


After that, add references to SystemWeaver API modules. 


Finally, import namespaces from the SystemWeaver API so that you can use them in Python code ("import SystemWeaverAPI as sw").


import clr
clr.AddReference('SystemWeaver.Connection')
clr.AddReference('SystemWeaver.Common')
clr.AddReference('SystemWeaver.ClientAPI')
import SystemWeaverAPI as sw
import SystemWeaver.Common as swc


In the python code, you can call SystemWeaver API methods by using "sw" (see below example of imported SystemWeaverAPI:

sw.SWConnection.Instance.LoginName = "student"


#! python
'''first python script'''

import sys
import clr
clr.AddReference('SystemWeaver.Connection')
clr.AddReference('SystemWeaver.Common')
clr.AddReference('SystemWeaver.ClientAPI')
import SystemWeaverAPI as sw
import SystemWeaver.Common as swc

def status_string(str_val):
    '''Status string'''
    return {
        0: 'Work',
        1: 'Frozen',
        2: 'Released',
        3: 'CSReleased',
        4: 'CheckedOut',
        5: 'NoAccess'
    }[str_val]

def datatype_name(dt_name):
    '''Datatypes'''
    return {
        0: 'Custom',
        1: 'Boolean',
        2: 'Computed',
        3: 'Custom',
        4: 'Date',
        5: 'Enumeration',
        6: 'ExtRef',
        7: 'Float',
        8: 'Identity',
        9: 'Integer',
        10: 'RVF',
        11: 'String',
        12: 'Text',
        13: 'User',
        14: 'XML'
    }[dt_name]

def attribute_type(attr):
    '''Attribute type'''
    attr_type = attr.AttributeType
    return attr_type.Name + ' ' + attr_type.Info + ' ' + str(attr_type.DataType)

def print_level(instr, level):
    '''Print with indent'''
    space = ' '
    print(level*2*space + instr)

def attribute_has_data(attr):
    '''Has attribute data'''
    return attr.IsNil != True

def print_attribute(item, level):
    '''Display attribute'''
    attr_sid = 'ADDT'
    attr = item.Attribute(attr_sid)
    if attribute_has_data(attr):
        print_level(attribute_type(attr))
    attr_sid = 'ABUN'
    attr = item.Attribute(attr_sid)
    if attribute_has_data(attr):
        print_level(attribute_type(attr) + ' ' + attr.ValueAsString, level)

def print_item(item, level):
    '''Display 1 item at a certain indentation - level'''
    print_level('item '+item.Name+' '+item.Version+' '+ item.CreatedBy.RealName+' '+
                item.CreationDate.Date.ToShortDateString() + ' '
                + swc.SWItemStatusExtensions.DisplayString(item.Status) + ' '
                + item.swItemType.SID, level)

def print_part(part, level):
    '''Display 1 part at a certain indentation - level'''
    print_level('part '+part.Name+' '
                + part.swPartType.SID, level)

def print_tree(parent, item_dictionary, level):
    '''Display tree'''
    if parent.HandleStr in item_dictionary:
        return
    item_dictionary[parent.HandleStr] = parent
    part_sids =  ['ITAP', 'ITDC', 'ITFC', 'ITIS', 'ITOS', 'DEMA']
    for part_sid in part_sids:
        parts = parent.GetParts(part_sid)
        for part in parts:
            item = part.DefObj
            print_part(part, level)
            print_item(item, level)
            print_attribute(item, level)
            print_tree(item, item_dictionary, level+1)

def load(handle_str):    
    """print a tree with handle as top item"""
    handle = swc.SWHandleUtility.ToHandle(handle_str)
    item_dictionary = dict()

    sw.SWConnection.Instance.LoginName = "student"
    sw.SWConnection.Instance.Password = "student"
    sw.SWConnection.Instance.ServerMachineName = "testServerIpAdress"
    sw.SWConnection.Instance.ServerPort = 1769
    sw.SWConnection.Instance.Login(0)

    sw.SWConnection.Instance.Ping()
    item = sw.SWConnection.Instance.Broker.GetItem(handle)
    print('---top---  \n' + item.Name)
    print_tree(item, item_dictionary, 1)


if __name__ == "__main__":
    load(sys.argv[1])

* If your organization has encryption enabled on the server, you must obtain the NuGet package from your IT department. The NuGet package published on Nuget.org does not allow for encryption.