Last year I wrote a series of articles discussing various ways in which Mo.net could be used in conjunction with Python to deliver modelling solutions leveraging the best of both worlds. In this latest article I will demonstrate another way of interacting directly with the Python kernel directly from a Mo.net project.
One of the great strengths of the Mo.net financial modelling platform is that it provides developers with access to the entire .NET ecosystem. This means that integrating with external libraries doesn’t have to involve writing temporary files, launching command-line processes, or parsing console output.
To use a Python library such as NumPy, Pandas, SciPy, or a bespoke machine learning model from within a Mo.net solution, the traditional solution is to invoke the Python script from a Mo.net task. But there is a more elegant way using Python.NET.
In this article I’ll demonstrate how to call Python functions directly from a Mo.net model using Python.NET, eliminating the overhead and complexity of shelling out to a separate Python process.
Why Avoid Shelling Out?
A common integration pattern is something like this:
- Export data to CSV or JSON.
- Launch Python script using the Mo.net Python task (so called “shelling out”)
- Wait for Python to finish.
- Read the output files back into the next Mo.net task.
Although this works, it has several disadvantages:
- Significant process startup overhead.
- Complicated error handling.
- Temporary file management.
- Difficult debugging.
- Limited interaction with Python objects.
- Poor performance when Python needs to be called repeatedly.
Python.NET embeds the Python interpreter directly inside your Mo.net project allowing Python functions to be called almost as if they were native Mo.net methods.
What is Python.NET?
Python.NET is an open-source bridge between .NET and CPython. Rather than translating Python code into .NET, it hosts the actual CPython runtime inside your Mo.net project. This means you can use virtually any Python package that works with your installed Python environment, including:
- NumPy
- Pandas
- SciPy
- scikit-learn
- TensorFlow
- PyTorch
- Proprietary Python modules
Prerequisites
The following components need to be downloaded / installed / configured before attempting to run the sample Mo.net project.
Python 3.14.0 or later (3.14.6 used in this example)
This should be installed before attempting to use Python.net with Mo.net.
https://www.python.org/downloads
Use the standalone installer if possible. Please perform a custom installation if possible and install Python into C:\Python314. Other installation locations are fine, but the sample code will need amending accordingly.
Python.NET
The easiest approach is to download Python.NET directly from the Nuget source:
https://www.nuget.org/packages/pythonnet
Click the Download Package link on the right hand side of the page and make a note of where the package has been downloaded (e.g. C:\Downloads).
Once the package has been downloaded, we need to unpack it. To do this create a folder in which to put the package contents, e.g. C:\Downloads\PythonNET.
Now unpack the contents of the Python.NET nuget package (this assumes version 3.1.0) into the folder created above by running this command from a Command or PowerShell prompt:
tar -xf "pythonnet.3.1.0.nupkg" -C "C:\Downloads\PythonNET"
.NET Framework 4.8 Components
We now need to ensure that some .NET Framework 4.8 components are installed. To do this we need to download the .NET Framework 4.8 offline installer from this location:
Once downloaded simply run the installer to install / confirm installation of .NET Framework 4.8 components.
With Python, PythonNET and .NET Framework 4.8 downloaded and installed we can now configure Mo.net to use them.
Preparing a Simple Python Script / Function
Before preparing the Mo.net project to use the Python.NET integration, we first need to prepare a simple Python function to use in Mo.net.
To do this, launch IDLE (the default Python code editor) and enter the following two line function:
def square(x):
return x * xSave this with a filename of mysquare.py in the C:\Python314 folder.
Preparing the Mo.net Project
We are now ready to create a Mo.net project that uses Python.NET to access the Python function created above.
- Start by creating a blank Mo.net project in Mo.net Model Development Studio, making a note of the location used as we will need this shortly.
- Copy the following files into the Mo.net project folder created above:
C:\Downloads\PythonNET\lib\netstandard2.0\Python.Runtime.dll, andC:\Windows\Microsoft.NET\Framework64\v4.0.30319\netstandard.dll (typical location, but it can vary) - Return to Mo.net Model Development Studio and add references to the two DLLs copied above by clicking on the References button in the Backstage (Info) view and selecting the files copied into the project folder.

- Finally we need to add into the Imports dialog (References -> Imports) Python.Runtime:

- We are now ready to test things with a simple solution
Simple Test
Create a new Group Projection task in the Mo.net project.
Add this code into the code editor window and run it:
Sub Run()
Runtime.PythonDLL = "C:\Python314\python314.dll"
PythonEngine.Initialize()
Using Py.GIL()
Dim sys = Py.Import("sys")
Dim path = sys.GetAttr("path")
path.InvokeMethod("append", New PyString("C:\Python314"))
Dim pyModule = Py.Import("mysquare")
Dim result = pyModule.InvokeMethod("square", New PyObject() {New PyInt(42)} )
Log.Progress("Result = " & (result.As(Of Integer)()).Tostring())
End Using
PythonEngine.Shutdown()
End SubWhat’s Going On?
The key elements of the Mo.net code are as follows.
| Line | Commentary |
| 3 | Tell Python.NET where the Python runtime DLL is located. This must point to the correct version of pythonXX.dll installed on the machine. |
| 5 | Start the Python interpreter so it can be used from Mo.net. |
| 7 | Acquire Python’s Global Interpreter Lock (GIL). The GIL must be held whenever interacting with Python objects |
| 9 | Import Python’s built-in “sys” module. |
| 10 | Get the “path” list from the sys module. sys.path contains the directories that Python searches for modules. |
| 11 | Add the Python installation folder to sys.path. This allows Python to locate modules stored in this directory. |
| 13 | Import the custom Python module named “mysquare.py”. The file must exist in one of the directories listed in sys.path. |
| 15 | Call the Python function “square” from the imported module. A Python integer (42) is passed as the function argument. The return value is stored as a PyObject. |
| 17 | Convert the returned PyObject into a Mo.net Integer and write the result to the application log. |
| 19 | Release the GIL automatically when leaving this block. |
| 21 | Shut down the Python interpreter and release its resources. This should be called when Python is no longer needed. |
Conclusion
I hope this series of articles has been useful and has helped illustrate the flexibility of the Mo.net platform. If there are any more Python integration use cases that you would like me to explore, please get in touch.