Package: iris-pex-embedded-python
1. interoperability-embedded-pythonThis proof of concept aims to show how theiris interoperability frameworkcan be use withembedded python.1.1. Table of Contents1. interoperability-embedded-python1.1. Table of Contents1.2. Example1.3. Register a component2. Demo3. Installation3.1. With ZPM3.2. With PyPI3.2.1. Known issues4. How to Run the Sample4.1. Docker containers4.2. Management Portal and VSCode4.3. Open the production5. What's inside the repository5.1. Dockerfile5.2. .vscode/settings.json5.3. .vscode/launch.json5.4. .vscode/extensions.json5.5. src folder6. How it works6.1. The__init__.pyfile6.2. Thecommonclass6.3. Thebusiness_hostclass6.4. Theinbound_adapterclass6.5. Theoutbound_adapterclass6.6. Thebusiness_serviceclass6.7. Thebusiness_processclass6.8. Thebusiness_operationclass6.8.1. The dispacth system6.8.2. The methods6.9. Thedirectorclass6.10. Theobjects6.11. Themessages6.12. How to regsiter a component6.12.1. register_component6.12.2. register_file6.12.3. register_folder6.12.4. migrate6.12.4.1. setting.py file6.12.4.1.1. CLASSES section6.12.4.1.2. Productions section6.13. Direct use of Grongier.PEX7. Command line7.1. help7.2. default7.3. lists7.4. start7.5. kill7.6. stop7.7. restart7.8. migrate7.9. export7.10. status7.11. version7.12. log8. Credits1.2. Examplebo.pyfromgrongier.peximportBusinessOperation,MessageclassMyBusinessOperation(BusinessOperation):defon_init(self):#This method is called when the component is becoming active in the productionself.log_info("[Python] ...MyBusinessOperation:on_init() is called")returndefon_teardown(self):#This method is called when the component is becoming inactive in the productionself.log_info("[Python] ...MyBusinessOperation:on_teardown() is called")returndefon_message(self,message_input:MyRequest):# called from service/process/operation, message is of type MyRequest with property request_stringself.log_info("[Python] ...MyBusinessOperation:on_message() is called with message:"+message_input.request_string)response=MyResponse("...MyBusinessOperation:on_message() echos")returnresponse@dataclassclassMyRequest(Message):request_string:str=None@dataclassclassMyResponse(Message):my_string:str=None1.3. Register a componentTo register a component, you need to create asetting.pyfile in the root of your project.This file will be used to register your classes and productions.e.g.:
setting.pyfromgrongier.peximportUtilsimportboCLASSES={"Python.MyBusinessOperation":bo.MyBusinessOperation}Then you can use theiopcommand line to register your component.iop--migrate/path/to/your/project/setting.py2. DemoYou can find a demo in thesrc/python/demofolder.Other demos are aviailable in those repositories:trainingtemplatefalskrest-to-dicom3. Installation3.1. With ZPMzpm "install pex-embbeded-python"3.2. With PyPIpip3installiris-pex-embedded-pythonImport the ObjectScript classes, open an embedded python shell and run :fromgrongier.peximportUtilsUtils.setup()or useiopcommand line :iop--init3.2.1. Known issuesIf the module is not updated, make sure to remove the old version :pip3uninstalliris-pex-embedded-pythonor manually remove thegrongierfolder in<iris_installation>/lib/python/or force the installation with pip :pip3install--upgradeiris-pex-embedded-python--target<iris_installation>/lib/python/4. How to Run the Sample4.1. Docker containersIn order to have access to the InterSystems images, we need to go to the following url:http://container.intersystems.com. After connecting with our InterSystems credentials, we will get our password to connect to the registry. In the docker VScode addon, in the image tab, by pressing connect registry and entering the same url as before (http://container.intersystems.com) as a generic registry, we will be asked to give our credentials. The login is the usual one but the password is the one we got from the website.From there, we should be able to build and compose our containers (with thedocker-compose.ymlandDockerfilefiles given).4.2. Management Portal and VSCodeThis repository is ready forVS Code.Open the locally-clonedinteroperability-embedeed-pythonfolder in VS Code.If prompted (bottom right corner), install the recommended extensions.IMPORTANT: When prompted, reopen the folder inside the container so you will be able to use the python components within it. The first time you do this it may take several minutes while the container is readied.By opening the folder remote you enable VS Code and any terminals you open within it to use the python components within the container. Configure these to use/usr/irissys/bin/irispython4.3. Open the productionTo open the production you can go toproduction.You can also click on the bottom on the127.0.0.1:52773[IRISAPP]button and selectOpen Management Portalthen, click on [Interoperability] and [Configure] menus then click [productions] and [Go].The production already has some code sample.Here we can see the production and our pure python services and operations:New json trace for python native messages :5. What's inside the repository5.1. DockerfileA dockerfile which install some python dependancies (pip, venv) and sudo in the container for conviencies.
Then it create the dev directory and copy in it this git repository.It starts IRIS and activates%Service_CallInforPython Shell.
Use the related docker-compose.yml to easily setup additional parametes like port number and where you map keys and host folders.This dockerfile ends with the installation of requirements for python modules.Use .env/ file to adjust the dockerfile being used in docker-compose.5.2. .vscode/settings.jsonSettings file to let you immedietly code in VSCode withVSCode ObjectScript plugin5.3. .vscode/launch.jsonConfig file if you want to debug with VSCode ObjectScriptRead about all the files in this article5.4. .vscode/extensions.jsonRecommendation file to add extensions if you want to run with VSCode in the container.More information hereThis is very useful to work with embedded python.5.5. src foldersrc
├── Grongier
│   └── PEX // ObjectScript classes that wrap python code
│       ├── BusinessOperation.cls
│       ├── BusinessProcess.cls
│       ├── BusinessService.cls
│       ├── Common.cls
│       ├── Director.cls
│       ├── InboundAdapter.cls
│       ├── Message.cls
│       ├── OutboundAdapter.cls
│       ├── Python.cls
│       ├── Test.cls
│       └── _utils.cls
├── PEX // Some example of wrapped classes
│   └── Production.cls
└── python
    ├── demo // Actual python code to run this demo
    |   `-- reddit
    |       |-- adapter.py
    |       |-- bo.py
    |       |-- bp.py
    |       |-- bs.py
    |       |-- message.py
    |       `-- obj.py
    ├── dist // Wheel used to implement python interoperability components
    │   └── grongier_pex-1.2.4-py3-none-any.whl
    ├── grongier
    │   └── pex // Helper classes to implement interoperability components
    │       ├── _business_host.py
    │       ├── _business_operation.py
    │       ├── _business_process.py
    │       ├── _business_service.py
    │       ├── _common.py
    │       ├── _director.py
    │       ├── _inbound_adapter.py
    │       ├── _message.py
    │       ├── _outbound_adapter.py
    │       ├── __init__.py
    │       └── _utils.py
    └── setup.py // setup to build the wheel6. How it works6.1. The__init__.pyfileThis file will allow us to create the classes to import in the code.It gets from the multiple files seen earlier the classes and make them into callable classes.
That way, when you wish to create a business operation, for example, you can just do:fromgrongier.peximportBusinessOperation6.2. ThecommonclassThe common class shouldn't be called by the user, it defines almost all the other classes.This class defines:on_init: The on_init() method is called when the component is started.Use the on_init() method to initialize any structures needed by the component.on_tear_down: Called before the component is terminated.Use it to free any structures.on_connected: The on_connected() method is called when the component is connected or reconnected after being disconnected.Use the on_connected() method to initialize any structures needed by the component.log_info: Write a log entry of type "info". :log entries can be viewed in the management portal.log_alert: Write a log entry of type "alert". :log entries can be viewed in the management portal.log_warning: Write a log entry of type "warning". :log entries can be viewed in the management portal.log_error: Write a log entry of type "error". :log entries can be viewed in the management portal.6.3. Thebusiness_hostclassThe business host class shouldn't be called by the user, it is the base class for all the business classes.This class defines:send_request_sync: Send the specified message to the target business process or business operation synchronously.Parameters:target: a string that specifies the name of the business process or operation to receive the request.The target is the name of the component as specified in the Item Name property in the production definition, not the class name of the component.request: specifies the message to send to the target. The request is either an instance of a class that is a subclass of Message class or of IRISObject class.If the target is a build-in ObjectScript component, you should use the IRISObject class. The IRISObject class enables the PEX framework to convert the message to a class supported by the target.timeout: an optional integer that specifies the number of seconds to wait before treating the send request as a failure. The default value is -1, which means wait forever.description: an optional string parameter that sets a description property in the message header. The default is None.Returns:
the response object from target.Raises:
TypeError: if request is not of type Message or IRISObject.send_request_async: Send the specified message to the target business process or business operation asynchronously.Parameters:target: a string that specifies the name of the business process or operation to receive the request.The target is the name of the component as specified in the Item Name property in the production definition, not the class name of the component.request: specifies the message to send to the target. The request is an instance of IRISObject or of a subclass of Message.If the target is a built-in ObjectScript component, you should use the IRISObject class. The IRISObject class enables the PEX framework to convert the message to a class supported by the target.description: an optional string parameter that sets a description property in the message header. The default is None.Raises:
TypeError: if request is not of type Message or IRISObject.get_adapter_type: Name of the registred Adapter.6.4. Theinbound_adapterclassInbound Adapter in Python are subclass from grongier.pex.InboundAdapter in Python, that inherit from all the functions of thecommon class.This class is responsible for receiving the data from the external system, validating the data, and sending it to the business service by calling the BusinessHost process_input method.
This class defines:on_task: Called by the production framework at intervals determined by the business service CallInterval property.The message can have any structure agreed upon by the inbound adapter and the business service.Example of an inbound adapter ( situated in the src/python/demo/reddit/adapter.py file ):fromgrongier.peximportInboundAdapterimportrequestsimportirisimportjsonclassRedditInboundAdapter(InboundAdapter):"""This adapter use requests to fetch self.limit posts as data from the redditAPI before calling process_input for each post."""defon_init(self):ifnothasattr(self,'feed'):self.feed="/new/"ifself.limitisNone:raiseTypeError('no Limit field')self.last_post_name=""return1defon_task(self):self.log_info(f"LIMIT:{self.limit}")ifself.feed=="":return1tSC=1# HTTP Requesttry:server="https://www.reddit.com"request_string=self.feed+".json?before="+self.last_post_name+"&limit="+self.limitself.log_info(server+request_string)response=requests.get(server+request_string)response.raise_for_status()data=response.json()updateLast=0forkey,valueinenumerate(data['data']['children']):ifvalue['data']['selftext']=="":continuepost=iris.cls('dc.Reddit.Post')._New()post._JSONImport(json.dumps(value['data']))post.OriginalJSON=json.dumps(value)ifnotupdateLast:self.LastPostName=value['data']['name']updateLast=1response=self.BusinessHost.ProcessInput(post)exceptrequests.exceptions.HTTPErroraserr:iferr.response.status_code==429:self.log_warning(err.__str__())else:raiseerrexceptExceptionaserr:self.log_error(err.__str__())raiseerrreturntSC6.5. Theoutbound_adapterclassOutbound Adapter in Python are subclass from grongier.pex.OutboundAdapter in Python, that inherit from all the functions of thecommon class.This class is responsible for sending the data to the external system.The Outbound Adapter gives the Operation the possibility to have a heartbeat notion.
To activate this option, the CallInterval parameter of the adapter must be strictly greater than 0.Example of an outbound adapter ( situated in the src/python/demo/reddit/adapter.py file ):classTestHeartBeat(OutboundAdapter):defon_keepalive(self):self.log_info('beep')defon_task(self):self.log_info('on_task')6.6. Thebusiness_serviceclassThis class is responsible for receiving the data from external system and sending it to business processes or business operations in the production.The business service can use an adapter to access the external system, which is specified overriding the get_adapter_type method.There are three ways of implementing a business service:Polling business service with an adapter - The production framework at regular intervals calls the adapter’s OnTask() method,
which sends the incoming data to the the business service ProcessInput() method, which, in turn calls the OnProcessInput method with your code.Polling business service that uses the default adapter - In this case, the framework calls the default adapter's OnTask method with no data.
The OnProcessInput() method then performs the role of the adapter and is responsible for accessing the external system and receiving the data.Nonpolling business service - The production framework does not initiate the business service. Instead custom code in either a long-running process
or one that is started at regular intervals initiates the business service by calling the Director.CreateBusinessService() method.Business service in Python are subclass from grongier.pex.BusinessService in Python, that inherit from all the functions of thebusiness host.This class defines:on_process_input: Receives the message from the inbond adapter via the PRocessInput method and is responsible for forwarding it to target business processes or operations.If the business service does not specify an adapter, then the default adapter calls this method with no message and the business service is responsible for receiving the data from the external system and validating it.Parameters:message_input: an instance of IRISObject or subclass of Message containing the data that the inbound adapter passes in.The message can have any structure agreed upon by the inbound adapter and the business service.Example of a business service ( situated in the src/python/demo/reddit/bs.py file ):fromgrongier.peximportBusinessServiceimportirisfrommessageimportPostMessagefromobjimportPostClassclassRedditServiceWithPexAdapter(BusinessService):"""This service use our python Python.RedditInboundAdapter to receive postfrom reddit and call the FilterPostRoutingRule process."""defget_adapter_type():"""Name of the registred Adapter"""return"Python.RedditInboundAdapter"defon_process_input(self,message_input):msg=iris.cls("dc.Demo.PostMessage")._New()msg.Post=message_inputreturnself.send_request_sync(self.target,msg)defon_init(self):ifnothasattr(self,'target'):self.target="Python.FilterPostRoutingRule"return6.7. Thebusiness_processclassTypically contains most of the logic in a production.A business process can receive messages from a business service, another business process, or a business operation.It can modify the message, convert it to a different format, or route it based on the message contents.The business process can route a message to a business operation or another business process.Business processes in Python are subclass from grongier.pex.BusinessProcess in Python, that inherit from all the functions of thebusiness host.This class defines:on_request: Handles requests sent to the business process. A production calls this method whenever an initial request for a specific business process arrives on the appropriate queue and is assigned a job in which to execute.Parameters:request: An instance of IRISObject or subclass of Message that contains the request message sent to the business process.Returns:
An instance of IRISObject or subclass of Message that contains the response message that this business process can return
to the production component that sent the initial message.on_response: Handles responses sent to the business process in response to messages that it sent to the target.A production calls this method whenever a response for a specific business process arrives on the appropriate queue and is assigned a job in which to execute.Typically this is a response to an asynchronous request made by the business process where the responseRequired parameter has a true value.Parameters:request: An instance of IRISObject or subclass of Message that contains the initial request message sent to the business process.response: An instance of IRISObject or subclass of Message that contains the response message that this business process can return to the production component that sent the initial message.callRequest: An instance of IRISObject or subclass of Message that contains the request that the business process sent to its target.callResponse: An instance of IRISObject or subclass of Message that contains the incoming response.completionKey: A string that contains the completionKey specified in the completionKey parameter of the outgoing SendAsync() method.Returns:
An instance of IRISObject or subclass of Message that contains the response message that this business process can return
to the production component that sent the initial message.on_complete: Called after the business process has received and handled all responses to requests it has sent to targets.Parameters:request: An instance of IRISObject or subclass of Message that contains the initial request message sent to the business process.response: An instance of IRISObject or subclass of Message that contains the response message that this business process can return to the production component that sent the initial message.Returns:
An instance of IRISObject or subclass of Message that contains the response message that this business process can return to the production component that sent the initial message.Example of a business process ( situated in the src/python/demo/reddit/bp.py file ):fromgrongier.peximportBusinessProcessfrommessageimportPostMessagefromobjimportPostClassclassFilterPostRoutingRule(BusinessProcess):"""This process receive a PostMessage containing a reddit post.It then understand if the post is about a dog or a cat or nothing andfill the right infomation inside the PostMessage before sending it tothe FileOperation operation."""defon_init(self):ifnothasattr(self,'target'):self.target="Python.FileOperation"returndefon_request(self,request):if'dog'.upper()inrequest.post.selftext.upper():request.to_email_address='dog@company.com'request.found='Dog'if'cat'.upper()inrequest.post.selftext.upper():request.to_email_address='cat@company.com'request.found='Cat'ifrequest.foundisnotNone:returnself.send_request_sync(self.target,request)else:return6.8. Thebusiness_operationclassThis class is responsible for sending the data to an external system or a local system such as an iris database.The business operation can optionally use an adapter to handle the outgoing message which is specified overriding the get_adapter_type method.If the business operation has an adapter, it uses the adapter to send the message to the external system.The adapter can either be a PEX adapter, an ObjectScript adapter or apython adapter.Business operation in Python are subclass from grongier.pex.BusinessOperation in Python, that inherit from all the functions of thebusiness host.6.8.1. The dispacth systemIn a business operation it is possbile to create any number of functionsimilar to the on_message methodthat will take as argument atyped requestlike thismy_special_message_method(self,request: MySpecialMessage).The dispatch system will automatically analyze any request arriving to the operation and dispacth the requests depending of their type. If the type of the request is not recognized or is not specified in anyon_message like function, the dispatch system will send it to theon_messagefunction.6.8.2. The methodsThis class defines:on_message: Called when the business operation receives a message from another production componentthat can not be dispatched to another function.Typically, the operation will either send the message to the external system or forward it to a business process or another business operation.
If the operation has an adapter, it uses the Adapter.invoke() method to call the method on the adapter that sends the message to the external system.
If the operation is forwarding the message to another production component, it uses the SendRequestAsync() or the SendRequestSync() method.Parameters:request: An instance of either a subclass of Message or of IRISObject containing the incoming message for the business operation.Returns:
The response objectExample of a business operation ( situated in the src/python/demo/reddit/bo.py file ):fromgrongier.peximportBusinessOperationfrommessageimportMyRequest,MyMessageimportirisimportosimportdatetimeimportsmtplibfromemail.mime.textimportMIMETextclassEmailOperation(BusinessOperation):"""This operation receive a PostMessage and send an email with all theimportant information to the concerned company ( dog or cat company )"""defmy_message(self,request:MyMessage):sender='admin@example.com'receivers='toto@example.com'port=1025msg=MIMEText(request.toto)msg['Subject']='MyMessage'msg['From']=sendermsg['To']=receiverswithsmtplib.SMTP('localhost',port)asserver:server.sendmail(sender,receivers,msg.as_string())print("Successfully sent email")defon_message(self,request):sender='admin@example.com'receivers=[request.to_email_address]port=1025msg=MIMEText('This is test mail')msg['Subject']=request.found+" found"msg['From']='admin@example.com'msg['To']=request.to_email_addresswithsmtplib.SMTP('localhost',port)asserver:# server.login('username', 'password')server.sendmail(sender,receivers,msg.as_string())print("Successfully sent email")If this operation is called using a MyRequest message, the my_message function will be called thanks to the dispatcher, otherwise the on_message function will be called.6.9. ThedirectorclassThe Director class is used for nonpolling business services, that is, business services which are not automatically called by the production framework (through the inbound adapter) at the call interval.Instead these business services are created by a custom application by calling the Director.create_business_service() method.This class defines:create_business_service: The create_business_service() method initiates the specified business service.Parameters:connection: an IRISConnection object that specifies the connection to an IRIS instance for Java.target: a string that specifies the name of the business service in the production definition.Returns:
an object that contains an instance of IRISBusinessServicestart_production: The start_production() method starts the production.Parameters:production_name: a string that specifies the name of the production to start.stop_production: The stop_production() method stops the production.Parameters:production_name: a string that specifies the name of the production to stop.restart_production: The restart_production() method restarts the production.Parameters:production_name: a string that specifies the name of the production to restart.list_productions: The list_productions() method returns a dictionary of the names of the productions that are currently running.6.10. TheobjectsWe will usedataclassto hold information in ourmessagesin aobj.pyfile.Example of an object ( situated in the src/python/demo/reddit/obj.py file ):fromdataclassesimportdataclass@dataclassclassPostClass:title:strselftext:strauthor:strurl:strcreated_utc:float=Noneoriginal_json:str=None6.11. ThemessagesThe messages will contain one or moreobjects, located in theobj.pyfile.Messages, requests and responses all inherit from thegrongier.pex.Messageclass.These messages will allow us to transfer information between any business service/process/operation.Example of a message ( situated in the src/python/demo/reddit/message.py file ):fromgrongier.peximportMessagefromdataclassesimportdataclassfromobjimportPostClass@dataclassclassPostMessage(Message):post:PostClass=Noneto_email_address:str=Nonefound:str=NoneWIP It is to be noted that it is needed to use types when you define an object or a message.6.12. How to regsiter a componentYou can register a component to iris in many way :Only one component withregister_componentAll the component in a file withregister_fileAll the component in a folder withregister_folder6.12.1. register_componentStart an embedded python shell :/usr/irissys/bin/irispythonThen use this class method to add a new py file to the component list for interoperability.fromgrongier.peximportUtilsUtils.register_component(<ModuleName>,<ClassName>,<PathToPyFile>,<OverWrite>,<NameOfTheComponent>)e.g :fromgrongier.peximportUtilsUtils.register_component("MyCombinedBusinessOperation","MyCombinedBusinessOperation","/irisdev/app/src/python/demo/",1,"PEX.MyCombinedBusinessOperation")6.12.2. register_fileStart an embedded python shell :/usr/irissys/bin/irispythonThen use this class method to add a new py file to the component list for interoperability.fromgrongier.peximportUtilsUtils.register_file(<File>,<OverWrite>,<PackageName>)e.g :fromgrongier.peximportUtilsUtils.register_file("/irisdev/app/src/python/demo/bo.py",1,"PEX")6.12.3. register_folderStart an embedded python shell :/usr/irissys/bin/irispythonThen use this class method to add a new py file to the component list for interoperability.fromgrongier.peximportUtilsUtils.register_folder(<Path>,<OverWrite>,<PackageName>)e.g :fromgrongier.peximportUtilsUtils.register_folder("/irisdev/app/src/python/demo/",1,"PEX")6.12.4. migrateStart an embedded python shell :/usr/irissys/bin/irispythonThen use this static method to migrate the settings file to the iris framework.fromgrongier.peximportUtilsUtils.migrate()6.12.4.1. setting.py fileThis file is used to store the settings of the interoperability components.It has two sections :CLASSES: This section is used to store the classes of the interoperability components.PRODUCTIONS: This section is used to store the productions of the interoperability components.e.g :importbpfromboimport*frombsimport*CLASSES={'Python.RedditService':RedditService,'Python.FilterPostRoutingRule':bp.FilterPostRoutingRule,'Python.FileOperation':FileOperation,'Python.FileOperationWithIrisAdapter':FileOperationWithIrisAdapter,}PRODUCTIONS=[{'dc.Python.Production':{"@Name":"dc.Demo.Production","@TestingEnabled":"true","@LogGeneralTraceEvents":"false","Description":"","ActorPoolSize":"2","Item":[{"@Name":"Python.FileOperation","@Category":"","@ClassName":"Python.FileOperation","@PoolSize":"1","@Enabled":"true","@Foreground":"false","@Comment":"","@LogTraceEvents":"true","@Schedule":"","Setting":{"@Target":"Host","@Name":"%settings","#text":"path=/tmp"}},{"@Name":"Python.RedditService","@Category":"","@ClassName":"Python.RedditService","@PoolSize":"1","@Enabled":"true","@Foreground":"false","@Comment":"","@LogTraceEvents":"false","@Schedule":"","Setting":[{"@Target":"Host","@Name":"%settings","#text":"limit=10\nother<10"}]},{"@Name":"Python.FilterPostRoutingRule","@Category":"","@ClassName":"Python.FilterPostRoutingRule","@PoolSize":"1","@Enabled":"true","@Foreground":"false","@Comment":"","@LogTraceEvents":"false","@Schedule":""}]}}]6.12.4.1.1. CLASSES sectionThis section is used to store the classes of the interoperability components.It aims to help to register the components.This dictionary has the following structure :Key : The name of the componentValue :The class of the component (you have to import it before)The module of the component (you have to import it before)Another dictionary with the following structure :module: Name of the module of the component (optional)class: Name of the class of the component (optional)path: The path of the component (mandatory)e.g :When Value is a class or a module:importboimportbpfrombsimportRedditServiceCLASSES={'Python.RedditService':RedditService,'Python.FilterPostRoutingRule':bp.FilterPostRoutingRule,'Python.FileOperation':bo,}When Value is a dictionary :CLASSES={'Python.RedditService':{'module':'bs','class':'RedditService','path':'/irisdev/app/src/python/demo/'},'Python.Module':{'module':'bp','path':'/irisdev/app/src/python/demo/'},'Python.Package':{'path':'/irisdev/app/src/python/demo/'},}6.12.4.1.2. Productions sectionThis section is used to store the productions of the interoperability components.It aims to help to register a production.This list has the following structure :A list of dictionary with the following structure :dc.Python.Production: The name of the production@Name: The name of the production@TestingEnabled: The testing enabled of the production@LogGeneralTraceEvents: The log general trace events of the productionDescription: The description of the productionActorPoolSize: The actor pool size of the productionItem: The list of the items of the production@Name: The name of the item@Category: The category of the item@ClassName: The class name of the item@PoolSize: The pool size of the item@Enabled: The enabled of the item@Foreground: The foreground of the item@Comment: The comment of the item@LogTraceEvents: The log trace events of the item@Schedule: The schedule of the itemSetting: The list of the settings of the item@Target: The target of the setting@Name: The name of the setting#text: The value of the settingThe minimum structure of a production is :PRODUCTIONS=[{'UnitTest.Production':{"Item":[{"@Name":"Python.FileOperation","@ClassName":"Python.FileOperation",},{"@Name":"Python.EmailOperation","@ClassName":"UnitTest.Package.EmailOperation"}]}}]You can also set in@ClassNamean item from the CLASSES section.e.g :fromboimportFileOperationPRODUCTIONS=[{'UnitTest.Production':{"Item":[{"@Name":"Python.FileOperation","@ClassName":FileOperation,}]}}]As the production is a dictionary, you can add in value of the production dictionary an environment variable.e.g :importosPRODUCTIONS=[{'UnitTest.Production':{"Item":[{"@Name":"Python.FileOperation","@ClassName":"Python.FileOperation","Setting":{"@Target":"Host","@Name":"%settings","#text":os.environ['SETTINGS']}}]}}]6.13. Direct use of Grongier.PEXIf you don't want to use the register_component util. You can add a Grongier.PEX.BusinessService component directly into the management portal and configure the properties :%module :Module name of your python code%classname :Classname of you component%classpathsPath where you component is.This can one or more Classpaths (separated by '|' character) needed in addition to PYTHON_PATHe.g :7. Command lineSince version 2.3.1, you can use the command line to register your components and productions.To use it, you have to use the following command :iopoutput :usage:python3-mgrongier.pex[-h][-dDEFAULT][-l][-sSTART][-k][-S][-r][-MMIGRATE][-eEXPORT][-x][-v][-L]optionalarguments:-h,--helpdisplayhelpanddefaultproductionname-dDEFAULT,--defaultDEFAULTsetthedefaultproduction-l,--listslistproductions-sSTART,--startSTARTstartaproduction-k,--killkillaproduction(forcestop)-S,--stopstopaproduction-r,--restartrestartaproduction-MMIGRATE,--migrateMIGRATEmigrateproductionandclasseswithsettingsfile-eEXPORT,--exportEXPORTexportaproduction-x,--statusstatusaproduction-v,--versiondisplayversion-L,--logdisplaylog

defaultproduction:PEX.Production7.1. helpThe help command display the help and the default production name.iop-houtput :usage:python3-mgrongier.pex[-h][-dDEFAULT][-l][-sSTART][-k][-S][-r][-MMIGRATE][-eEXPORT][-x][-v][-L]...
defaultproduction:PEX.Production7.2. defaultThe default command set the default production.With no argument, it display the default production.iop-doutput :defaultproduction:PEX.ProductionWith an argument, it set the default production.iop-dPEX.Production7.3. listsThe lists command list productions.iop-loutput :{"PEX.Production":{"Status":"Stopped","LastStartTime":"2023-05-31 11:13:51.000","LastStopTime":"2023-05-31 11:13:54.153","AutoStart":0}}7.4. startThe start command start a production.To exit the command, you have to press CTRL+C.iop-sPEX.Productionoutput :2021-08-3015:13:51.000[PEX.Production]INFO:Startingproduction2021-08-3015:13:51.000[PEX.Production]INFO:StartingitemPython.FileOperation2021-08-3015:13:51.000[PEX.Production]INFO:StartingitemPython.EmailOperation
...7.5. killThe kill command kill a production (force stop).Kill command is the same as stop command but with a force stop.Kill command doesn't take an argument because only one production can be running.iop-k7.6. stopThe stop command stop a production.Stop command doesn't take an argument because only one production can be running.iop-S7.7. restartThe restart command restart a production.Restart command doesn't take an argument because only one production can be running.iop-r7.8. migrateThe migrate command migrate a production and classes with settings file.Migrate command must take the absolute path of the settings file.Settings file must be in the same folder as the python code.iop-M/tmp/settings.py7.9. exportThe export command export a production.If no argument is given, the export command export the default production.iop-eIf an argument is given, the export command export the production given in argument.iop-ePEX.Productionoutput :{"Production":{"@Name":"PEX.Production","@TestingEnabled":"true","@LogGeneralTraceEvents":"false","Description":"","ActorPoolSize":"2","Item":[{"@Name":"Python.FileOperation","@Category":"","@ClassName":"Python.FileOperation","@PoolSize":"1","@Enabled":"true","@Foreground":"false","@Comment":"","@LogTraceEvents":"true","@Schedule":"","Setting":[{"@Target":"Adapter","@Name":"Charset","#text":"utf-8"},{"@Target":"Adapter","@Name":"FilePath","#text":"/irisdev/app/output/"},{"@Target":"Host","@Name":"%settings","#text":"path=/irisdev/app/output/"}]}]}}7.10. statusThe status command status a production.Status command doesn't take an argument because only one production can be running.iop-xoutput :{"Production":"PEX.Production","Status":"stopped"}Status can be :stoppedrunningsuspendedtroubled7.11. versionThe version command display the version.iop-voutput :2.3.07.12. logThe log command display the log.To exit the command, you have to press CTRL+C.iop-Loutput :2021-08-3015:13:51.000[PEX.Production]INFO:Startingproduction2021-08-3015:13:51.000[PEX.Production]INFO:StartingitemPython.FileOperation2021-08-3015:13:51.000[PEX.Production]INFO:StartingitemPython.EmailOperation
...8. CreditsMost of the code came from PEX for Python by Mo Cheng and Summer Gerry.Works only on IRIS 2021.2 +