[IronPython] built-in modules

Dino Viehland dinov at microsoft.com
Mon Nov 23 06:13:47 CET 2009


I started writing up some documentation around this as part of our push to actually have useful documentation :)  Here's the current version of that - if you have any feedback or additional questions it'd be great to hear to improve the docs.

1. Implementing new modules in .NET
When creating a new module usually you would just implement it in IronPython its self. But sometimes you may have requirements that preclude implementing it in IronPython. These could be due to needing a .NET library which IronPython cannot fully or easily consume (because it requires attributes, for example), due to performance, or other reasons. When you run into this road block you have one of two options.
First, you can simply expose this functionality as a normal .NET library and allow the user to interact with it through the normal IronPython .NET interop mechanisms. Alternately you can implement a Python module in your favorite .NET language. If you're not entirely sure which one to choose you're lucky because there's not much difference between the two - but in this section we'll cover how to implement a Python module.
The first thing to do is to create a new project, or open an existing one, and add a reference to IronPython.dll.
The next step is to define a new class and add an assembly level PythonModuleAttribute which points at the class giving the module name and type. After doing this you should have a file which looks like:
C# example:
using System;
using IronPython.Runtime;

[assembly: PythonModule("my_module", typeof(MyModule))]

public static class MyModule {
}
VB.NET example:
Imports System
Imports IronPython.Runtime

<Assembly: PythonModule("my_module", GetType(MyModule))>

Public Module MyModule

End Module
Consuming from Python:
>>> import clr
>>> clr.AddReference('test')
>>> import my_module
>From here you just need to start implementing the functionality of your module. You can start to add methods, fields, properties, or types. All the member must be static as there will not be an instance of the module created.
Here's an example where we define a method, a property, and a couple of fields. One of the fields is a literal and the other is a static field. One important detail to note is that modules are entirely read-only. Even if you implement a property or a mutable field IronPython will never set a value into the property or field. Instead the updated field will always be stored in the modules dictionary.
C# example:
using System;
using IronPython.Runtime;

[assembly: PythonModule("my_module", typeof(MyModule))]

public static class MyModule {
    public static void hello_world() {
        Console.WriteLine("hello world");
    }

    public static string foo {
        get {
            return "foo";
        }
    }

    public const string bar = "bar";

    public static readonly string baz = "baz";
}
VB.NET example:
Imports System
Imports IronPython.Runtime

<Assembly: PythonModule("my_module", GetType(MyModule))>

Public Module MyModule
    Public Sub hello_world()
        Console.WriteLine("Hello World")
    End Sub

    Public ReadOnly Property foo As String
        Get
            Return "foo"
        End Get
    End Property

    Public Const bar As String = "bar"

    Public ReadOnly baz As String = "baz"
End Module
Consuming from Python:
>>> import clr
>>> clr.AddReference('test')
>>> import my_module
>>> my_module.hello_world()
hello world
>>> my_module.foo
'foo'
>>> my_module.bar
'bar'
>>> my_module.baz
'baz'
1.1. Initialization / Reloading
If your module requires to run specific code to be initialized you can provide a method marked with SpecialNameAttribute which receives the PythonContext the module is running in as well as the PythonDictionary where the module members will live.
This example will make "some_name" available in the module and it will have the value "Hello World".
C# example:
using System;
using System.Runtime.CompilerServices;
using IronPython.Runtime;

[assembly: PythonModule("my_module", typeof(MyModule))]

public static class MyModule {
    [SpecialName]
    public static void PerformModuleReload(PythonContext context, PythonDictionary dict) {
        dict["some_name"] = "Hello World";
    }
}
VB.NET example:
Imports System
Imports IronPython.Runtime
Imports System.Runtime.CompilerServices

<Assembly: PythonModule("my_module", GetType(MyModule))>

Public Module MyModule
    <SpecialName> _
    Public Sub PerformModuleReload(ByVal context As PythonContext, ByVal dict As PythonDictionary)
        dict("some_name") = "Hello World"
    End Sub
End Module
Consuming from Python:
>>> import clr
>>> clr.AddReference('test')
>>> import my_module
>>> my_module.some_value
'Hello World'
>>>
1.2. Per-Runtime State
Because modules are static classes you need to have somewhere you can store state for the current IronPython runtime instance. If you were to store it in a static field this state would bleed between IronPython runtimes in the same app domain. To accomodate this the PythonContext has a set of APIs which are specifically designed for storing state for Python modules. These APIs can be used either in the PerformModuleReload method or in methods which receive CodeContext as their first parameter. The CodeContext object will automatically be flowed in and from it you can get the PythonContext from the LanguageContext property.
C# example:
using System;
using IronPython.Runtime;

[assembly: PythonModule("my_module", typeof(MyModule))]

public static class MyModule {
    private static readonly object _stateKey = new object();

    public static object get_state(CodeContext context, object value) {
        object prev_value = null;

        if (context.LanguageContext.HasModuleState(_stateKey)) {
            prev_value = context.LanguageContext.GetModuleState(_stateKey);
        }

        context.LanguageContext.SetModuleState(_stateKey, value);

        return prev_value;
    }
}
VB.NET example:
Imports System
Imports IronPython.Runtime

<Assembly: PythonModule("my_module", GetType(MyModule))>

Public Module MyModule
    dim _stateKey as object = new object()

    Public Function get_state(ByVal context As CodeContext, ByVal value As Object) As Object
        Dim moduleState As Object = Nothing

        If context.LanguageContext.HasModuleState(MyModule._stateKey) Then
            moduleState = context.LanguageContext.GetModuleState(MyModule._stateKey)
        End If

        context.LanguageContext.SetModuleState(MyModule._stateKey, value)
        Return moduleState
    End Function
End Module
Consuming from Python:
>>> import clr
>>> clr.AddReference('test')
>>> from my_module import get_state
>>> get_state(42)
>>> get_state(23)
42
1.2.1. Runtime State API Reference
public object GetModuleState(object key)
Gets per-runtime state used by a module. The module should have a unique key for each piece of state it needs to store.
public void SetModuleState(object key, object value)
Sets per-runtime state used by a module. The module should have a unique key for each piece of state it needs to store.
public object GetSetModuleState(object key, object value)
Sets per-runtime state used by a module and returns the previous value. The module should have a unique key for each piece of state it needs to store.
public T GetOrCreateModuleState<T>(object key, Func<T> value)
Gets the the module state if a value already exists. If one does not exist then calls the provided delegate to create the new value. The module should have a unique key for each piece of state it needs to store.
bool HasModuleState(object key)
Checks to see if module state has the current value stored already.
1.3. Deploying Modules
So far in all of the examples we've seen the Python consumer has been required to call clr.AddReference on the module containing the DLL before it can be used. Obviously this is less than ideal as it both requires import clr (which has the side effect of making .NET members available) as well as requiring the AddReference call itself.
ipy.exe also supports automatically loading modules which have been deployed to a directory named "DLLs" next to ipy.exe. Therefore once you've finished developing your module you can copy it over and it will always be available.


From: users-bounces at lists.ironpython.com [mailto:users-bounces at lists.ironpython.com] On Behalf Of Slide
Sent: Sunday, November 22, 2009 9:04 PM
To: Discussion of IronPython
Subject: [IronPython] built-in modules

Are there any tutorials on writing modules that appear as built-ins? does it have to be part of the IronPython.Modules assembly to be loaded correctly?

thanks,

slide

--
slide-o-blog
http://slide-o-blog.blogspot.com/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/ironpython-users/attachments/20091123/957d4d1f/attachment.html>


More information about the Ironpython-users mailing list