Showing posts with label ironpython. Show all posts
Showing posts with label ironpython. Show all posts

Wednesday, February 2, 2011

IronPython & WPF: Data binding with TreeView's selected element

In this post I'm going to show a small example of using data binding with the selected element of a WPF TreeView with an IronPython class.

A couple of days ago I had the necessity of using data binding to keep track of the selected value of a WPF TreeView . At first it seemed to be an easy task so I wrote:

<TreeView SelectedValue="{Binding selected, Mode=TwoWay}" ... />


Running this code results on the following error:

SystemError: 'Provide value on 'System.Windows.Data.Binding' threw an exception.' Line number '12' and line position '7'.


The problem is that the SelectedValue (and SelectedItem) property is read-only.

There are several ways to deal with this problem. One alternative is to use a technique similar to the one described in the "Forwarding the Result of WPF Validation in MVVM" post. We're going to define an attached property which works as an "output only" property that could be used with data binding.

Attached property definition



I couldn't find a way to wrote the definition of the attached property in IronPython because it needed to be instanciated by XamlReader. So the definition was written using C#:

using System.Windows.Markup;
using System.Windows;
using System.Windows.Controls;
using System.IO;
using System.Collections.ObjectModel;


namespace Langexplr
{
public static class TreeViewSelectedBehavior
{
public static readonly DependencyProperty MySelectedProperty =
System.Windows.DependencyProperty.RegisterAttached(
"MySelected",
typeof(object),
typeof(TreeViewSelectedBehavior)
);
public static object GetMySelected(TreeView t)
{
return t.GetValue(MySelectedProperty);
}

public static void SetMySelected(TreeView t, object theValue)
{
t.SetValue(MySelectedProperty, theValue);
}


public static readonly DependencyProperty SelectedHelperProperty =
System.Windows.DependencyProperty.RegisterAttached(
"SelectedHelper",
typeof(TreeViewSelectedHelper),
typeof(TreeViewSelectedBehavior),
new UIPropertyMetadata(null,OnSelectedHelperChanged)
);

public static TreeViewSelectedHelper GetSelectedHelper(TreeView t)
{
return (TreeViewSelectedHelper)t.GetValue(SelectedHelperProperty);
}

public static void SetSelectedHelper(TreeView t,
TreeViewSelectedHelper theValue)
{
t.SetValue(SelectedHelperProperty, theValue);
}
static void OnSelectedHelperChanged(
DependencyObject depObj,
DependencyPropertyChangedEventArgs e)
{
((TreeViewSelectedHelper)e.NewValue).Register((TreeView)depObj);
}

}


This class define two properties:
  • MySelected: the output property that is used to set the selected element in the view model
  • SelectedHelper: which is used to as an object that modifies the value of MySelected when the selected item changes(see below).


The following helper class is used to subscribe the SelectedItemChanged event and change "MySelected" .

public class TreeViewSelectedHelper 
{
public TreeViewSelectedHelper() { }
void SelectedItemChanged(object sender,
RoutedPropertyChangedEventArgs<object> args)
{
(sender as TreeView).SetValue(
TreeViewSelectedBehavior.MySelectedProperty,
((sender as TreeView)).SelectedItem);
}
public void Register(TreeView t)
{
t.SelectedItemChanged += SelectedItemChanged;
}
}

}


We can now compile this class:


set NETFX4=c:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\
set WPFPATH=%NETFX4%\WPF
csc /debug /r:%NETFX4%System.Xaml.dll /r:%WPFPATH%\WindowsBase.dll /r:%WPFPATH%\PresentationCore.dll /r:%WPFPATH%\PresentationFramework.dll /target:library utils.cs


The example



Having defined this attached property and helper class we can now write the following example.

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:utils="clr-namespace:Langexplr;assembly=utils"
Title="TreeView selection test" Width="300" Height="300">
<Window.Resources>
<utils:TreeViewSelectedHelper x:Key="selHelper" />
</Window.Resources>

<StackPanel>

<TextBlock Text="{Binding selected.label}"/>
<TreeView ItemsSource="{Binding roots}"
utils:TreeViewSelectedBehavior.SelectedHelper="{StaticResource selHelper}">
<utils:TreeViewSelectedBehavior.MySelected>
<Binding Path="selected" Mode="OneWayToSource"/>
</utils:TreeViewSelectedBehavior.MySelected>

<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding children}">
<TextBlock Text="{Binding label}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
</Window>


With this XAML definition we can write the following IronPython code:

import clr
clr.AddReference("PresentationCore")
clr.AddReference("PresentationFramework")
clr.AddReference("WindowsBase")
clr.AddReference('GalaSoft.MvvmLight.WPF4.dll')


from System.Windows.Markup import XamlReader
from System.Windows import Application
from System.IO import File
from System.Windows.Controls import TreeView
import System
import clrtype
from GalaSoft.MvvmLight import ViewModelBase
from System.Collections.ObjectModel import ObservableCollection


class TestModel(ViewModelBase):
__metaclass__ = clrtype.ClrClass
def __init__(self):
self.name = 'algo'
self.root = NodeModel('x1',[NodeModel('y2',[NodeModel('z2',[])]),
NodeModel('y3',[])])

self.sselected = NodeModel('',[])

@property
@clrtype.accepts()
@clrtype.returns(System.Object)
def selected(self):
result = self.sselected
return result

@selected.setter
@clrtype.accepts(System.Object)
@clrtype.returns()
def selected(self, value):
self.sselected = value
self.RaisePropertyChanged('selected')

@property
@clrtype.accepts()
@clrtype.returns(System.Object)
def roots(self):
return [self.root]

@property
@clrtype.accepts()
@clrtype.returns(System.String)
def label(self):
return self.name

@label.setter
@clrtype.accepts(System.String)
@clrtype.returns()
def label(self, value):
self.name = value
self.RaisePropertyChanged('label')



class NodeModel:
__metaclass__ = clrtype.ClrClass

def __init__(self,label, initchildren):
self.children_collection = ObservableCollection[System.Object](initchildren)
self.name = label


@property
@clrtype.accepts()
@clrtype.returns(System.Object)
def label(self):
return self.name

@property
@clrtype.accepts()
@clrtype.returns(System.Object)
def children(self):
return self.children_collection


xamlFile = File.OpenRead('test.xaml')
window = XamlReader.Load(xamlFile)
window.DataContext = TestModel()
xamlFile.Close()

Application().Run(window)


By running this example we can see how the label of the selected element of the TreeView is reflected in the TextBlock defined above.

Monday, January 31, 2011

IronPython & Silverlight Part VI: Using the TreeView control

In this post I'm going to show a small example of using the Silverlight 4 TreeView control with IronPython.

Referencing System.Windows.Controls.dll


Before we start using the TreeView control we need to add a reference to System.Windows.Controls.dll . This assembly can be found in the "Microsoft Silverlight 4 Tools for Visual Studio 2010" package.

You need to copy this assembly to the location of chiron.exe (ex. IronPython2.7\Silverlight\bin) and add a reference to it in the AppManifest.xaml file.

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
RuntimeVersion="4.0.50917.0"
EntryPointAssembly="Microsoft.Scripting.Silverlight"
EntryPointType="Microsoft.Scripting.Silverlight.DynamicApplication"
ExternalCallersFromCrossDomain="ScriptableOnly">
<!-- Add assembly references here -->
<Deployment.Parts>
...
<AssemblyPart Source="System.Windows.Controls.dll" />
</Deployment.Parts>
...
</Deployment>


Since we're using Silverlight 4 we need to change the runtime version to "4.0.50917.0" (see this post for more details).

Using the TreeView in XAML


Now that we have access to the assembly we can use it in app.xaml.

<UserControl x:Class="System.Windows.Controls.UserControl"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:wsdk="clr-namespace:System.Windows;assembly=System.Windows.Controls">
<StackPanel x:Name="layout_root" Background="White">
<TextBlock x:Name="Message" FontSize="30" Text="TreeView experiment" />
<sdk:TreeView x:Name="tree">
<sdk:TreeView.ItemTemplate>
<wsdk:HierarchicalDataTemplate ItemsSource="{Binding nodes}">
<TextBlock Foreground="Red" Text="{Binding label}" />
</wsdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
</StackPanel>
</UserControl>



TreeView data binding


The XAML file above shows that we're binding the content of the tree view to the nodes property of the data source object. Here's the definition of the Python class used for this example:

from System.Windows import Application
from System.Windows.Controls import UserControl
from System import Object
from System.Collections.ObjectModel import ObservableCollection
import clrtype
import clr

clr.AddReferenceToFile('GalaSoft.MvvmLight.SL4.dll')

from GalaSoft.MvvmLight.Command import RelayCommand


class Node:
__metaclass__ = clrtype.ClrClass
__clrnamespace = "LangexplrExperiments"


def __init__(self,label,children):
self.text = label
self.children = children

@property
@clrtype.accepts()
@clrtype.returns(str)
def label(self):
return self.text

@property
@clrtype.accepts()
@clrtype.returns(Object)
def nodes(self):
return self.children



class App:
def __init__(self):
root = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
root.tree.ItemsSource = [Node('level1',
[Node('level11',[]),
Node('level12',[Node('level121',[])])
])]


App()


With this changes we can run chiron.exe /b and we get:

Saturday, January 29, 2011

IronPython & Silverlight Part V: Using the MVVM Light Toolkit

In this post I'm going to show a small example of using the MVVM Light Toolkit in Silverlight with IronPython.

MVVM Light


According to its website, MVVM Light Toolkit is a:

...set of components helping people to get started in the Model - View - ViewModel pattern in Silverlight and WPF...

It provides useful elements such an implementation of "Relay Command" and a feature to expose events as commands (EventToCommand).

In the context of IronPython it will save us a lot of code.

Adding a reference to MVVM Light


There are several ways to add a reference to the MVVM Light Toolkit assembly. However the first step is to set the Silverlight version to 4 (see this post for more information).

Adding the reference using the manifest


One way to add the reference is to use the manifest AppManifest.xaml. Once we generated this file(using chiron.exe /m and copying it to app/) and changed the Silverlight version to 4 (see here) we can add an entry referencing the GalaSoft.MvvmLight.SL4.dll assembly.

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
RuntimeVersion="4.0.50401.0"
EntryPointAssembly="Microsoft.Scripting.Silverlight"
EntryPointType="Microsoft.Scripting.Silverlight.DynamicApplication"
ExternalCallersFromCrossDomain="ScriptableOnly">
<!-- Add assembly references here -->
<Deployment.Parts>
...
<AssemblyPart Source="GalaSoft.MvvmLight.SL4.dll"/>
...
</Deployment.Parts>
...
</Deployment>

Also we need to copy of the GalaSoft.MvvmLight.SL4.dll file to the chiron.exe folder (ex. IronPython\Silvelright\bin).


Using it in Python


Once we referenced this file using the manifest we need to reference it from code.
import clr
clr.AddReferenceToFile('GalaSoft.MvvmLight.SL4.dll')


Example


The following example shows the use of RelayCommand and ViewModelBase from IronPython.

app.xaml:

<UserControl x:Class="System.Windows.Controls.UserControl"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wbc="clr-namespace:System.Windows.Controls">
<StackPanel Width="300" x:Name="layout_root" Background="White">
<TextBox x:Name="my_text_box"
Text="{Binding text_to_display, Mode=TwoWay}" />

<TextBlock Text="{Binding text_to_display}" />
<Button Content="Reset" Command="{Binding reset_command}"/>
</StackPanel>
</UserControl>


app.py:

import clr
clr.AddReferenceToFile('GalaSoft.MvvmLight.SL4.dll')
from System.Windows import Application
from System.Windows.Controls import UserControl
import System
import clrtype
from System.Windows import MessageBox
from GalaSoft.MvvmLight.Command import RelayCommand
from GalaSoft.MvvmLight import ViewModelBase

class MyBasicModel(ViewModelBase):
__metaclass__ = clrtype.ClrClass

def __init__(self):
self.text = 'Initial text'

@property
@clrtype.accepts()
@clrtype.returns(System.String)
def text_to_display(self): return self.text


@text_to_display.setter
@clrtype.accepts(System.String)
@clrtype.returns()
def text_to_display(self, value):
self.text = value
self.RaisePropertyChanged('text_to_display')

@property
@clrtype.accepts()
@clrtype.returns(System.Object)
def reset_command(self):
return RelayCommand(lambda: self.perform_reset_text() )

def perform_reset_text(self):
self.text_to_display = ''


class App:

def __init__(self):

self.model = MyBasicModel()
self.root = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
self.root.DataContext = self.model


theApp = App()


Using this library from IronPython saves us from many thins such as having to declare events to comply with the INotifyPropertyChanged interface.

Tuesday, January 25, 2011

IronPython & Silverlight Part IV: Using Silverlight 4

The default Silverlight runtime version of programs created with IronPython 2.7 Beta 1 is "2.0.31005.0". If you want to take advantage of Silverlight 4 features you have to make a small change to AppManifest.xaml.


For example, say that you want to add a RichTextBox control(available on Silverlight 4).

<UserControl x:Class="System.Windows.Controls.UserControl"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wbc="clr-namespace:System.Windows.Controls">
<StackPanel Width="300" x:Name="layout_root" Background="White">
<wbc:RichTextBox x:Name="rtb" />
<Button x:Name="my_button" Content="Ok"/>
</StackPanel>
</UserControl>



And

from System.Windows import Application
from System.Windows.Controls import UserControl
import System


class App:

def __init__(self):
self.theText = """
this
is
some
text"""



self.root = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
self.root.rtb.Selection.Select(self.root.rtb.ContentStart, self.root.rtb.ContentEnd)
self.root.rtb.Selection.Text = self.theText

theApp = App()



In order to make this program work you have to add the AppManifest.xaml to the app/ folder of your application. You can get a copy of this file using chiron.exe /m :

By default this file looks like this:

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
RuntimeVersion="2.0.31005.0"
EntryPointAssembly="Microsoft.Scripting.Silverlight"
EntryPointType="Microsoft.Scripting.Silverlight.DynamicApplication"
ExternalCallersFromCrossDomain="ScriptableOnly">
<!-- Add assembly references here -->
<Deployment.Parts>
<!-- In the XAP -->
<!-- <AssemblyPart Source="Foo.dll" /> -->
<!-- Outside the XAP, same domain -->
<!-- <AssemblyPart Source="/Foo.dll" /> -->
<!-- Outside the XAP, different domain -->
<!-- <AssemblyPart Source="http://bar.com/Foo.dll" /> -->
<AssemblyPart Source="Microsoft.Scripting.Silverlight.dll" />
<AssemblyPart Source="System.Numerics.dll" />
<AssemblyPart Source="Microsoft.Scripting.dll" />
<AssemblyPart Source="Microsoft.Dynamic.dll" />
<AssemblyPart Source="IronPython.dll" />
<AssemblyPart Source="IronPython.Modules.dll" />
</Deployment.Parts>
<!-- Add transparent platform extensions (.slvx) references here -->
<Deployment.ExternalParts>
<!-- Example -->
<!-- <ExtensionPart Source="http://bar.com/v1/Foo.slvx" /> -->
</Deployment.ExternalParts>
...
</Deployment>


After copying this file to the app/ folder you have to change the RuntimeVersion attribute value to "4.0.50401.0".

Now to can run the application and have access to Silverlight 4 features.

Wednesday, December 15, 2010

IronPython & Silverlight Part III: Basic Data Binding

One of the nicest features of Silverlight is data binding. This feature allows you to perform and receive changes on the UI without explicitly adding or removing elements from UI controls. For example:

<TextBox Text="{Binding TextToDisplay}">


This XAML code says that the value of the Text property is bound to the TextToDisplay property of the of the object specified by the DataContext property.

As with other Silverlight features data binding requires you to use a .NET object with properties. We can use clrtype to take advantage of this feature with IronPython.

For example, say that we want to bind a text property to a Python object to a TextBox:

<UserControl x:Class="System.Windows.Controls.UserControl"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Width="300" x:Name="layout_root" Background="White">
<TextBox x:Name="my_text_box"
Text="{Binding text_to_display, Mode=TwoWay}" />
<Button x:Name="my_button" Content="Show text"/>
</StackPanel>
</UserControl>



The app.py file looks like this:


from System.Windows import Application
from System.Windows.Controls import UserControl
import System
import clrtype
from System.Windows import MessageBox

class MyData:
__metaclass__ = clrtype.ClrClass

def __init__(self):
self.text = 'Initial text'

@property
@clrtype.accepts()
@clrtype.returns(System.String)
def text_to_display(self): return self.text


@text_to_display.setter
@clrtype.accepts(System.String)
@clrtype.returns()
def text_to_display(self, value):
self.text = value

class App:

def handle_click(self, sender, event_args):
MessageBox.Show(self.data.text)

def __init__(self):
self.data = MyData()
self.root = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
self.root.my_text_box.DataContext = self.data
self.root.my_button.Click += lambda s,ea: self.handle_click(s,ea)

theApp = App()


Here the definition of MyData is decorated with the information on how to generate the .NET class elements to be exposed .


Since the binding is declarated as TwoWay modifications to the TextBox are reflected in the data instance.




As with Part I and Part II of these series, IronPython 2.7 beta 1 is used for all examples.

Tuesday, December 7, 2010

IronPython & Silverlight Part II: Basic event handling

There are a couple of ways to add event handlers to Silverlight controls. The common way is to add the event handlers directly in XAML. For example:

<Button x:Name="ButtonGo" Content="Go!" Click="MyClickHandler" />


Given that there's a definition for the MyClickHandler method in your C# code. However for IronPython there's a couple of options:

Directly in Python code



Event handlers can be added as in C# by using the '+=' operator. For example say that you have the following XAML code describing an UserControl:

<UserControl x:Class="System.Windows.Controls.UserControl"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<StackPanel Width="300" x:Name="layout_root" Background="White">
<TextBlock x:Name="Message" FontSize="30" />
<Button x:Name="ButtonGo" Content="Go!" />
</StackPanel>
<UserControl.Resources>
<Storyboard x:Name="MyStoryboard">
<DoubleAnimation
Storyboard.TargetName="Message"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:3"
AutoReverse="True"
RepeatBehavior="Forever"/>
</Storyboard>
</UserControl.Resources>
</UserControl>


Say we want to start the 'MyStoryboard' animation in the event handler for the Click event of the ButtonGo button. We can write:

from System.Windows import Application
from System.Windows.Controls import UserControl

def handle_click(sender, event_args):
theApp.root.MyStoryboard.Begin()

class App:

def __init__(self):
self.root = Application.Current.LoadRootVisual(MyUserControl(), "app.xaml")
self.root.Message.Text = "Welcome to Python and Silverlight!"
self.root.ButtonGo.Click += handle_click

theApp = App()


Using clrtype



The clrtype module can be used to define .NET classes from (Iron)Python classes . This module can be found in the IronPython samples package or here in the GitHub repository. The GUI Automated Testing blog has some nice tutorials on using clrtype.

To use this module, you have to copy the clrtype.py file to your app/ folder.

We can change the code this way:

from System.Windows import Application
from System.Windows.Controls import UserControl
from System.Windows import MessageBox
import System
import clrtype

class MyUserControl(UserControl):
__metaclass__ = clrtype.ClrClass

_clrnamespace = "MyNs"

@clrtype.accepts(System.Object, System.EventArgs)
@clrtype.returns(System.Void)
def my_click_handler(self, sender, event_args):
theApp.root.MyStoryboard.Begin()


def __getattr__(self, name):
return self.FindName(name)

def handle_click(sender,event_args):
theApp.root.MyStoryboard.Begin()

class App:

def __init__(self):
self.root = Application.Current.LoadRootVisual(MyUserControl(), "app.xaml")
self.root.Message.Text = "Welcome to Python and Silverlight!"

theApp = App()


With these definitions we can change the XAML code for the Button to have be:

<Button x:Name="ButtonGo" Content="Go!" Click="my_click_handler"/>


Notice that the definition of MyUserControl has a definition for __getattr__. This definition is used to still be able to access the definitions of child controls for example theApp.root.MyStoryboard.

Final words



Another way to do event handling is to use Commanding. This approach is preferred for MVVM. For future posts I'll try to cover the use of Commanding with IronPython.

Tuesday, November 30, 2010

Using IronPython with Silverlight, Part I

This is the first of series of posts on the topic of using IronPython to create Silverlight programs. Experimenting with these technologies is nice because you only need a text editor and the IronPython distribution.

Getting started



In these series of posts I'll be using IronPython 2.7 (which is in beta right now) and Silverlight 4 .

Once these packages are installed the first step is to copy a basic program template to a your work directory. The template is located in (IronPython path)\Silverlight\script\templates\python.

This template contains the following structure:


C:.
¦ index.html
¦
+---app
¦ app.py
¦ app.xaml
¦
+---css
¦ screen.css
¦
+---js
error.js


The app.py and app.xaml files contain the code for the entry point of the demo Silverlight application. The index.html file contains the Silverlight control host.

The default app.xaml code looks like this:

<UserControl x:Class="System.Windows.Controls.UserControl"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Grid x:Name="layout_root" Background="White">
<TextBlock x:Name="Message" FontSize="30" />
</Grid>

</UserControl>


The default app.py code looks like this:

from System.Windows import Application
from System.Windows.Controls import UserControl

class App:
def __init__(self):
root = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
root.Message.Text = "Welcome to Python and Silverlight!"

App()


As you can see this code loads the XAML file for defining the 'root visual' . The 'root.Message.Text = ...' assignment changes the text displayed by the 'Message' TextBlock instance. Notice that it uses the name of the control as if it were a member of the UserControl instance returned by LoadRootVisual. This is accomplished by using a DLR feature defined in the FrameworkElementExtension class (see ExtensionTypes.cs )

You can easily run this example by going to the command line and running Chiron:

C:\development\blog\ipy\basictest>c:\development\IronPython-2.7\Silverlight\bin\Chiron.exe /b
Chiron - Silverlight Dynamic Language Development Utility. Version 1.0.0.0
Chiron serving 'C:\development\blog\ipy\basictest' as http://localhost:2060/
21:03:45 200 1,185 /
21:03:45 200 792 /style.css!
21:03:45 200 2,492 /sl.png!
21:03:45 200 642 /slx.png!
21:03:49 404 580 /favicon.ico [Resource not found]


Running Chiron this way will start the web server and will open a web browser in its root.


(Note: For IronPython 2.7 Beta you have to copy the System.Numerics.dll assembly in the %IPY_HOME\Silverlight\bin folder, this file could be found in the Silverlight SDK distribution).

When you select the index.html file the Silverlight program is executed.



As you can see an IronPython REPL console is activated by default in this template. This console is activated by using the following tag in the HTML file.

<param name="initParams" value="reportErrors=errorLocation, console=true" />


The nice thing about this is that you can manipulate the Silverlight application while is executing. To see an example of this, we can change the definition of the App class to be like this:

class App:

def __init__(self):
self.root = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
self.root.Message.Text = "Welcome to Python and Silverlight!"

theApp = App()


With this change we can now have access to the UI elements defined in XAML. For example:



For future posts I'm going to talk about specific topics on the use of Silverlight and IronPython.

A nice source of information on this topic is the following page by Michael Foord "Python in your Browser with Silverlight"

Wednesday, October 13, 2010

Using C#'s implicit type conversions from other .NET languages

One interesting C# feature is the ability to define a method that implements implicit conversion from one type to another. In this post I'm going to show how to use this feature from IronPython, F#, VB.NET and IronRuby.

Example



In order to illustrate the implicit conversion feature we're going to use the following classes:


namespace Langexplr.Experiments
{
public class Complex
{
public double Real { get; set; }
public double Img { get; set; }

public static implicit operator Complex(double real)
{
return new Complex() { Real = real };
}

public static implicit operator Polar(Complex complex)
{
return new Polar() { Angle = Math.Atan(complex.Img/complex.Real),
Length = Math.Sqrt(complex.Real*complex.Real +
complex.Img*complex.Img) };
}

public static implicit operator double(Complex complex)
{
return Math.Sqrt(complex.Real*complex.Real +
complex.Img*complex.Img);
}

}

public class Polar
{
public double Angle { get; set; }
public double Length { get; set; }
}
}



The Complex class is a simple definition of a complex number. The Polar class is defined(conveniently) to represent a complex number in polar form. The Complex class defines three implicit conversions:

  1. From double to a complex number

  2. From Complex to Polar

  3. From Complex to double


The following C# code shows a use of this feature:


using Langexplr.Experiments;
using System;

class main
{
public static void Main(string[] args)
{
Complex c = 10.3;
Polar p = new Complex() {Real = 12.3, Img = 5.2};
double abs = c;

Console.WriteLine("abs:{0} Polar: {1},{2}", abs ,p.Angle ,p.Length);
}
}


By looking at the definitions generated by the compiler for the Complex class, we can see several definitions for the op_Implicit method with different parameters and return types.

...
.method public hidebysig specialname static
class Langexplr.Experiments.Complex
op_Implicit(float64 real) cil managed
...
.method public hidebysig specialname static
class Langexplr.Experiments.Polar
op_Implicit(class Langexplr.Experiments.Complex complex) cil managed
...
.method public hidebysig specialname static
float64 op_Implicit(class Langexplr.Experiments.Complex complex) cil managed
...



Now these uses of the Complex class will be presented on different .NET languages.

IronPython



As described in "Dark Corners of IronPython" by Michael Foord the clr.Convert function can be used to convert between types using the op_Implicit if necessary.

For example:


import clr
clr.AddReference("ImplicitTest")

from Langexplr.Experiments import *
from System import Double

c = clr.Convert(10.3, Complex)
nC = Complex()
nC.Real = 12.3
nC.Img = 5.2
p = clr.Convert(nC, Polar)
abs = clr.Convert(c, Double)

print 'abs: %(0)f Polar: %(1)f,%(2)f\n' % \
{ '0': abs, '1' : p.Angle, '2' : p.Length }


IronRuby



IronRuby will use the op_Implicit definition if a conversion required at a particular call. I couldn't find a nice way to do this directly as with IronPython's clr.Convert . However the following function definition seems to do the trick:


def dotnet_convert(value,type)
f = System::Func[type,type].new {|x| x}
f.invoke(value)
end


This conversion function works since IronRuby tries to convert the value to the expected .NET type in the call to 'invoke' .

Using this definition we can write:


require 'ImplicitTest.dll'

c = dotnet_convert(10.3,Langexplr::Experiments::Complex)
nC = Langexplr::Experiments::Complex.new
nC.Real = 12.3
nC.Img = 5.2
p = dotnet_convert(nC,Langexplr::Experiments::Polar)
abs = dotnet_convert(c,System::Double)

print "abs: #{abs} Polar: #{p.Angle},#{p.Length} \n"



F#



In F# we can call the op_Implicit method directly and F# will use type inference to determine the correct overload to use.

For example:

open Langexplr.Experiments

let c : Complex = Complex.op_Implicit 10.3
let p : Polar = Complex.op_Implicit (new Complex(Real=12.3, Img=5.2))
let abs : double = Complex.op_Implicit c

System.Console.WriteLine("1. {0} Polar: {1},{2} ", abs, p.Angle, p.Length )



There's a nice post called "F# – Duck Typing and Structural Typing" by Matthew Podwysocki, which describes a nice way to define a generic function to use the op_Implicit operator.


let inline convert (x:^a) : ^b = ((^a or ^b) : (static member op_Implicit : ^a -> ^b) x )


This function can be used as follows:


let c2:Complex = convert 10.3
let p2:Polar = convert (new Complex(Real=12.3, Img=5.2))
let abs2:float = convert c

System.Console.WriteLine("2. {0} Polar: {1},{2} ",abs2,p2.Angle,p2.Length)


VB.NET



Finally in Visual Basic .NET the implicit conversion is used automatically as in C#. For example:


Imports System
Imports Langexplr.Experiments
Module Test
Sub Main
Dim c As Complex = 10.3
Dim p As Polar = new Complex() With { _
.Real = 12.3, _
.Img = 5.2 _
}
Dim abs As Double = c
Console.WriteLine("abs:{0} Polar: {1},{2}",abs,p.Angle,p.Length)
End Sub
End Module

Monday, August 3, 2009

Creating Dynamic JSON array finders using the DLR

One of the things that really impressed me while reading about Ruby on Rails was the use of method_missing to implement dynamic finders. The technique is described on the "How dynamic filters work". In this post I'm going to show a little experiment of creating a similar technique for querying JSON arrays using the .NET's Dynamic Language Runtime infrastructure.

Code for this post was created using Visual Studio 2010 Beta 1, IronRuby for .NET 4 beta 1 and IronPython 2.6 beta 4 for .NET 4.

JSON.NET



For this post I'm using the JSON.NET library for loading the JSON data. This library provides several ways of loading JSON data. Here I'll be using a set of predefined classes: JObject for JSON objects, JArray for arrays, JValue for literal values, etc. All these classes inherit from JToken.
Code in this post use the JSON data returned by the Twitter REST API. An example of this data:


[
{"in_reply_to_screen_name":null,
"text":"...",
"user": { "following":null,
"description":"...",
"screen_name":"...",
"utc_offset":0,
"followers_count":10,
"time_zone":"...",
"statuses_count":155,
"created_at":"...",
"friends_count":1,
"url":"...",
"name":"...",
"notifications":null,
"protected":false,
"verified":false,
"favourites_count":0,
"location":"...",
"id": ...,
...
},
"truncated":false,
"created_at":"...",
"in_reply_to_status_id":null,
"in_reply_to_user_id":null,
"favorited":false,
"id":...,
"source":"...."
},
...
]



Dynamic queries on JSON data



Finders will be implemented for JSON arrays. As with Rail's dynamic finders the names of the required fields will be encoded in the name of the invoked method.

The following C# 4.0 code shows an example of this wrapper class in conjunction with the dynamic keyword.


JsonTextReader reader = new JsonTextReader(rdr);
JsonSerializer serializer = new JsonSerializer();
JArray o = (JArray)serializer.Deserialize(reader);
dynamic dArray = new FSDynArrayWrapper(o);

string name = "ldfallas";
foreach (var aJObject in dArray.FindAllByFavoritedAlsoByUserWithScreen_Name("false",name))
{
dynamic tobj = new FSDynJObjectWrapper(aJObject);
Console.WriteLine("========");
Console.WriteLine(tobj.user.screen_name);
Console.Write("\t'{0}'",tobj.text);
}




A small definition of the syntax used for names is the following.


method-name = "FindAllBy" ,
property-name , ("With", property-name)? ,
("AlsoBy" property-name , ("With", property-name)? ) *
property-name = valid json property name


In order to be more flexible the following syntax will also be allowed:


method-name = "find_all_by_" ,
property-name , ("_with_", property-name)? ,
("_also_by" property-name , ("_with_", property-name)? ) *
property-name = valid json property name


A sample name for this query methods look like this:


array.FindAllByFavoritedAlsoByUserWithScreen_Name("true","ldfallas")


This method will accept two parameters and is going to :


Find all the object elements from the array that has a 'Favorited' property equal to 'true' and also has an object with a 'User' property associated with an object which has a 'Screen_Name' property which is equal to the 'ldfallas'


Interoperability



One of the nice things of using the DLR infrastructure to create this feature, is that it can be used by other DLR languages. The following example is an IronRuby snippet:

require 'FsDlrJsonExperiments.dll'
include Langexplr::Experiments

while true do
print "Another try\n"
str = System::Net::WebClient.new().download_string("http://twitter.com/statuses/public_timeline.json")
json = FSDynArrayWrapper.CreateFromReader(System::IO::StringReader.new(str))

for i in json.find_all_by_user_with_time_zone('Central America') do
print i.to_string()
end
sleep(5)
end



The following IronPython code shows a little example of this wrapper class.


fReader = StreamReader(GetTwitterPublicTimeline())
jReader = JsonTextReader(fReader)
serializer = JsonSerializer()

json = FSDynArrayWrapper( serializer.Deserialize(jReader) )

for i in json.FindAllByFavoritedAlsoByUserWithScreen_Name("false","ldfallas"):
print i


Implementation



In order to implement the functionality presented here, the IDynamicMetaObjectProvider interface and the DynamicMetaObject class were used. By using these we can generate the code for the call site as a expression tree. For more information on how to use this interface see
Getting Started with the DLR as a Library Author document (available here) .

The code generated to do the filtering is an expression which uses the Where method from System.Linq.Enumerable
. The generated expression written in source using a pseudo C# looks like this:


{
object tmp;
array.Where(c => (((c Is JObject) &&
CompareHelper(
GetJObjectPropertyCI(((JObject)c), "Favorited"),
"true"))
&&
((((tmp = GetJObjectPropertyCI(((JObject)c), "User")) As JObject) != null) &&
CompareHelper(
GetJObjectPropertyCI(((JObject)tmp), "Screen_Name"),
"ldfallas"))))

}


Where GetJObjectPropertyCI is a helper method that gets a property from a JObject by case-intensive name . And CompareHelper is a helper method to do the comparison.

The implementation for FSDynArrayWrapper was written in F#. Mainly because it's a nice language to implement this kind of features. However there's no easy way to consume this feature using F# since it doesn't use the DLR.

Here's the definition:


type FSDynArrayWrapper(a:JArray) =
member this.array with get() = a
static member CreateFromReader(stream : System.IO.TextReader) =
...
static member CreateFromFile(fileName:string) =
...
interface IDynamicMetaObjectProvider with
member this.GetMetaObject( parameter : Expression) : DynamicMetaObject =
FSDynArrayWrapperMetaObject(parameter,this) :> DynamicMetaObject


As you can see, the interesting part is in the implementation of FSDynArrayWrapperMetaObject. The CreateFromReader and CreateFromFile methods are only utility methods to load data from a document.

The implementation of FSDynArrayWrapperMetaObject looks like this:


type FSDynArrayWrapperMetaObject(expression : Expression, value: System.Object) =
inherit DynamicMetaObject(expression,BindingRestrictions.Empty,value)

...

override this.BindInvokeMember(binder : InvokeMemberBinder, args: DynamicMetaObject array) =
match QueryInfo.GetQueryElements(binder.Name) with
| Some( elements ) ->
(new DynamicMetaObject(
this.GenerateCodeForBinder(
elements,
Array.map
(fun (v:DynamicMetaObject) ->
Expression.Constant(v.Value.ToString()) :> Expression) args),
binder.FallbackInvokeMember(this,args).Restrictions))
| None -> base.BindInvokeMember(binder,args)


The BindInvokeMember creates the expression tree for the code that will be executed for a given invocation of a dynamic finder method. Here the QueryInfo.GetQueryElements method is called to extract the elements of the name as described above. The value returned by this method is QueryElement list option where:


type QueryElement =
| ElementQuery of string
| SubElementQuery of string * string


ElementQuery specifies the "Favorited" part in FindAllByFavoritedAlsoByUserWithScreen and the SubElementQuery belongs to the "ByUserWithScreen" part in FindAllByFavoritedAlsoByUserWithScreen .

If the name of the invoked method corresponds is a supported name for a finder, the GenerateCodeForBinder is called to generate the expression tree. The last argument of this method is a collection of the arguments provided for this invocation.


member this.GenerateCodeForBinder(elements, arguments : Expression array) =
let whereParameter = Expression.Parameter(typeof<JToken>, "c") in
let tmpVar = Expression.Parameter(typeof<JToken>, "tmp") in
let whereMethodInfo =
(typeof<System.Linq.Enumerable>).GetMethods()
|> Seq.filter (fun (m:MethodInfo) -> m.Name = "Where" && (m.GetParameters().Length = 2))
|> Seq.map (fun (m:MethodInfo) -> m.MakeGenericMethod(typeof<JToken>))
|> Seq.hd
let queryElementsConditions =
elements
|> Seq.zip arguments
|> Seq.map
(fun (argument,queryParameter) ->
this.GetPropertyExpressionForQueryArgument(queryParameter,argument,whereParameter,tmpVar)) in
let initialCondition = Expression.TypeIs(whereParameter,typeof<JObject>) in

let resultingExpression =
Expression.Block(
[tmpVar],
Expression.Call(
whereMethodInfo,
Expression.Property(
Expression.Convert(
this.Expression,this.LimitType),"array"),
Expression.Lambda(
Seq.fold
(fun s c -> Expression.And(s,c) :> Expression)
(initialCondition :> Expression)
queryElementsConditions,
whereParameter))) in
resultingExpression



The most important parts of this method is the definition of queryElementsConditions and resultingExpression. The resulting expression specifies the invocation to the Where method


The queryElementsConditions take each argument extracted from the name of the method and tries to generate the necessary conditions for the value provided as an argument. In order to do this the GetPropertyExpressionForQueryArgument method is used:


member this.GetPropertyExpressionForQueryArgument(parameter:QueryElement,argument,cParam,tmpVar) : Expression =
match parameter with
| ElementQuery(propertyName) ->
this.CompareExpression(
this.GetJObjectPropertyExpression(cParam,propertyName) ,
argument)
| SubElementQuery(propertyName,subPropertyName) ->
Expression.And(
Expression.NotEqual(
Expression.TypeAs(
Expression.Assign(
tmpVar,
this.GetJObjectPropertyExpression(cParam,propertyName)),
typeof<JObject>),
Expression.Constant(null)),
this.CompareExpression(
this.GetJObjectPropertyExpression(
tmpVar,
subPropertyName),argument)) :> Expression



This method generates a different expression depending on the kind of query element that is requested.

Considerations for IronPython



As a curious note, in IronPython the BindInvokeMember method is not called in the FSDynArrayWrapperMetaObject when a method is invoked. It seems that IronPython calls the BindGetMember method and then tries to apply the result of getting the method.

So to make this object work with IronPython a implementation of the BindGetMember method was created that returns a lambda expression tree with the generated Where invocation.


override this.BindGetMember(binder: GetMemberBinder) =
match QueryInfo.GetQueryElements(binder.Name) with
| Some( elements ) ->
let parameters =
List.mapi ( fun i _ ->
Expression.Parameter(
typeof<string>,
sprintf "p%d" i)) elements
(new DynamicMetaObject(
Expression.Lambda(
this.GenerateCodeForBinder(
elements,
parameters
|> List.map (fun p -> p :> Expression)
|> List.to_array ),
parameters),
binder.FallbackGetMember(this).Restrictions))
| None -> base.BindGetMember(binder)


Accessing using different names



The QueryInfo.GetQueryElements method is used to allow the "FindAllBy..." and "find_all_by..." method names .


module QueryInfo = begin

...

let GetQueryElements(methodName:string) =
match methodName with
| str when str.StartsWith("FindAllBy") ->
Some(ExtractQueryElements(str.Substring("FindAllBy".Length),"AlsoBy","With"))
| str when str.StartsWith("find_all_by_") ->
Some(ExtractQueryElements(str.Substring("find_all_by_".Length),"_also_by_","_with_") )
| _ -> None
end


Code


Code for this post can be found here.

Thursday, February 12, 2009

Writing Xml with IronPython, XmlWriter and the 'with' statement

This post shows a little example of wrapping calls to System.Xml.XmlWriter inside a Python's 'with' statement using IronPython.

Writing Xml



While reading some code examples from the XIST HTML/XML generation library I noticed the nice use of Python's 'with' statement to represent the target HTML or XML.

The System.Xml.XmlWriter class provided with .NET already gives you a way to write well formed Xml documents. In this post I'm going to show how to use an XmlWriter instance in conjunction with Python's 'with' statement.

We want to write the following code:


from __future__ import with_statement

...

w = XmlWriter.Create(System.Console.Out,XmlWriterSettings(Indent=True))
x = XWriter(w)

with x.element('tableofcontents'):
with x.element('section',{'page' : '10'}):
x.text('Introduction')
with x.element('section',{'page' : '12'}):
x.text('Main topic')
with x.element('section',{'page' : '14'}):
x.text('Extra topic')


To generate the following Xml file:


<tableofcontents>
<section page="10">Introduction</section>
<section page="12">Main topic</section>
<section page="14">Extra topic</section>
</tableofcontents>


The 'with' statement



The 'with' statement was introduced in Python 2.5 . This statement is used wrap the execution of a series of statements with some special code. For example it is used to implement the try...except...finally pattern.

As described in the documentation the following statement:


with context expression:
statements...


Will be executed as follows:


  1. Evaluate the context expression to obtain the context manager

  2. Invoke the context manager's __enter__() method

  3. Execute the statements

  4. When the execution of the statements finishes(even with an exception), the context manager's __exit()__ method is called.



Given these steps we're going to implement a context manager that assist in the creation of Xml documents using the System.Xml.XmlWriter .NET class.

The following code shows a class that wraps the XmlWriter instance and helps with the creation of context managers:


class XWriter(object):
def __init__(self,writer):
self.writer = writer

def element(self,name,atts = {}):
return ElementCtxt(name,atts,self)

def nselement(self,prefix,name,namespace,atts = {}):
return NamespaceElementCtxt(prefix,name,namespace,atts,self)


def text(self,text):
self.writer.WriteString(text)

def cdata(self,text):
self.writer.WriteCData(text)



Notice that the element method creates an instance of the ElementCtxt class using the element name and an optional dictionary with the attributes. As the following listing shows this class performs the calls to WriteStartElement and WriteEndElement in the __enter__ and __exit__ methods.


class ElementCtxt(object):
def __init__(self,elementName,atts,writer):
self.elementName = elementName
self.atts = atts
self.writer = writer

def processAttributes(self):
for att in self.atts:
self.writer.writer.WriteAttributeString(att,self.atts[att].__str__())

def processStartTag(self):
self.writer.writer.WriteStartElement(self.elementName)
self.processAttributes()

def __enter__(self):
self.processStartTag()
return self

def __exit__(self,t,v,tr):
self.writer.writer.WriteEndElement()
return t == None



The XWriter.nselement method is used to write elements with namespace and prefix. This call generates an instance of the following context manager:


class NamespaceElementCtxt(ElementCtxt):
def __init__(self,prefix,elementName,namespace,atts,writer):
ElementCtxt.__init__(self,elementName,atts,writer)
self.namespace = namespace
self.prefix = prefix
def processStartTag(self):
self.writer.writer.WriteStartElement(self.prefix,self.elementName,self.namespace)
self.processAttributes()


Final example



The following code shows how to create a little SVG file:


from __future__ import with_statement
from xmlwriterw import XWriter

import clr

clr.AddReference('System.Xml')

from System.Xml import *
import System


w = XmlWriter.Create(System.Console.Out,\
XmlWriterSettings(Indent=True))
x = XWriter(w)

svgNs = 'http://www.w3.org/2000/svg'

with x.nselement('s','svg',svgNs,{'version': '1.1',
'viewBox': '0 0 100 100',
'style':'width:100%; height:100%; position:absolute; top:0; left:0; z-index:-1;'}):
with x.nselement('s','linearGradient',svgNs, { 'id' : 'gradient' }):
with x.nselement('s','stop',svgNs, {'class' : 'begin',
'offset' : '0%',
'stop-color':'red'}):
pass
with x.nselement('s','stop',svgNs, {'class' : 'end',
'offset' : '100%'}):
pass
with x.nselement('s','rect',svgNs, { 'x':0,
'y':0,
'width':100,
'height':100,
'style':'fill:url(#gradient)'} ):
pass
for i in range(1,5):
with x.nselement('s','circle',svgNs, { 'cx': 50,
'cy': 50,
'r': 30 - i*3,
'style':'fill:url(#gradient)'} ):
pass

w.Close()


Running this program shows:


<s:svg viewBox="0 0 100 100" style="width:100%; height:100%; position:absolute; top:0; left:0; z-index:-1;" version="1.1" xmlns:s="http://www.w3.org/2000/svg">
<s:linearGradient id="gradient">
<s:stop offset="0%" class="begin" stop-color="red" />
<s:stop offset="100%" class="end" />
</s:linearGradient>
<s:rect x="0" height="100" width="100" style="fill:url(#gradient)" y="0" />
<s:circle cx="50" cy="50" style="fill:url(#gradient)" r="27" />
<s:circle cx="50" cy="50" style="fill:url(#gradient)" r="24" />
<s:circle cx="50" cy="50" style="fill:url(#gradient)" r="21" />
<s:circle cx="50" cy="50" style="fill:url(#gradient)" r="18" />
</s:svg>

Wednesday, January 21, 2009

Some notes on using F# code from IronPython

This post presents a couple of things I learned about the interaction between IronPython and F#.

Most information about this topic can be found by looking at the code generated by the F# compiler, using tools like ILDASM or Reflector. Also the F# from C# section of the F# 1.1.12 documentation is very helpful.

FSharp.Code library



In order to call some F# basic functionality we need to add a reference to FSharp.Core.

Python

import clr
clr.AddReference('FSharp.Core')



Type parameters



IronPython provides a way to specify type parameters to methods and classes by using square brackets followed by the names of the types to be used. This mechanism can be used to create F# constructs that require them.

The following example shows how to create an F# list:

Python

>>> import clr
>>> clr.AddReference('FSharp.Core')
>>> from Microsoft.FSharp.Collections import List
>>> l1 = List[str].Cons('World',List[str].Empty)
>>> l2 = List[str].Cons('Hello',l1)
>>> " ".join(l2)
'Hello World'



Type parameters, for methods that require them, also can be specified using the same syntax. For example the following code shows the use of Seq.length with an Python sequence.

Python

print Seq.length[int]([45,234,52,345,2,346,657])



Tuples



Tuples in F# are instances of the Microsoft.FSharp.Core.Tuple class (with several definitions with depending on the number of arguments). A tuple instance contains properties Item# where # is the position of the element.

The following example shows how to use a tuple created in F# from IronPython.

F#

module MyFunctions = begin
...
let numberEvenTest x = x,x % 2 = 0
...
end


Python

print MyFunctions.numberEvenTest(46)
print MyFunctions.numberEvenTest(46).Item1
print MyFunctions.numberEvenTest(46).Item2



This program will print:


(46,True)
46
True


To create a tuple inside IronPython the Tuple[type1,type2,...](value1,value2,...) constructor must be used.

For example to given the following definition:

F#

module MyFunctions = begin
...
let movePoints points xdistance ydistance =
Seq.map (fun (x,y) -> (x+xdistance,y+ydistance)) points
...
end


We can used from IronPython like this:
Python

from Microsoft.FSharp.Core import Tuple
...
points = [ Tuple[int,int](1,2), Tuple[int,int](5,3) ]

for p in MyFunctions.movePoints(points,20,40):
print p


It is tempting to use the Python syntax for assigning a list of variables to the values of the tuples. For example:


x,y,z = 2,3,5


However this is not possible since F# tuples are not compatible with IronPython sequences.

Operators



F# operators are created using the same conventions as the C# operators. Thus they can be used directly in IronPython.

For example the following F# definition:


type Complex(real:double,img:double) = class
member this.real with get() = real
member this.img with get() = img
static member (+) (c1:Complex,c2:Complex) =
Complex(c1.real+c2.real,c1.img+c2.img)
override this.ToString() = sprintf "%f + %fi" real img
end


Can be used in IronPython:

Python


c1 = Complex(20,234)
c2 = Complex(34.35,32.2)
c3 = (c1+c2)



Discriminated unions



Discriminated unions in F# provide a compact way to define data structures. The following example shows the definition of simple math expressions that include addition, subtraction and numeric literals.

F#

type MathExpr =
| Addition of MathExpr * MathExpr
| Subtraction of MathExpr * MathExpr
| Literal of double


The following code snippet shows how to create instance of these math expression elements from IronPython.

Python

expr = MathExpr.Addition(MathExpr.Literal(10),\
MathExpr.Subtraction(\
MathExpr.Literal(40), \
MathExpr.Literal(24)))


A instance of the discriminated union has methods IsXXXX to verify the kind. Also properties a generated with the name of the constructor followed by a number (for example Addition1) to access values given to the constructor.

The following example shows how to evaluate a math expression.

Python

def evalExpr(expr):
if expr.IsAddition():
return evalExpr(expr.Addition1) + evalExpr(expr.Addition2)
elif expr.IsSubtraction():
return evalExpr(expr.Subtraction1) - evalExpr(expr.Subtraction2)
else:
return expr.Literal1


Functions



Functions passed as parameters need to be converted to a special F# element that represent them. In order to do this the FuncConvert.ToFastFunc function is used.

For example the following code shows how to call Seq.iter to print all the elements of a Python list.


from Microsoft.FSharp.Core import FuncConvert
...
aList = [5,3,42,6]

def foo(x):
print(x)

Seq.iter[int](FuncConvert.ToFastFunc[int]( foo),aList)



Since IronPython and F# use IEnumerable<T> types to represent collections and sequences we can use F# functions to manipulate IronPython collections.

For example given the following definition of a Python generator for Fibonacci numbers:


def fibo():
last1 = 0
last2 = 1
while True:
yield last1
next = last1+last2
last2 = last1
last1 = next


We can call F# functions to process values generated by fibo. For example the following IronPython code prints the first 15 squared Fibonacci numbers.



for n in Seq.map[int,int]( FuncConvert.ToFastFunc[int,int](lambda x: x*x) ,\
Seq.take[int](15,fibo())):
print n




Code for this post was created using IronPython 2.0 and F# 1.9.6.2 .