The notebooks IPython offer a couple of magic command to run others language such %%R, %%octave or %%julia. I found one option for F# but nothing on something like %%CS. However, magic commands are quite easy to handle. It is not that difficult to add one which allows me to do that:
%%CS mypower System.dll public static double mypower(double x, double y) { if (y == 0) return 1.0 ; return System.Math.Pow(x,y) ; }
To be able to call it that way:
mypower(3.0,3.0)
pythonnet offers a simple way to call C# from a Python program. Based on that, creating a magic command requires three parts. The first one is a C# DLL which dynamically compiles a code.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.CodeDom.Compiler; using Microsoft.CSharp; using System.Reflection; namespace MagicIPython { public static class MagicCS { private const string embedCode = @" namespace MagicCSIPython {0} public static class MagicCSFunctions_{2} {0} {3} {1} {1} "; public static MethodInfo CreateFunction(string functionName, string code, string[] dependencies) { CSharpCodeProvider provider = new CSharpCodeProvider(); CompilerParameters parameters = new CompilerParameters(); if (dependencies != null) { foreach (var d in dependencies) parameters.ReferencedAssemblies.Add(d); } parameters.GenerateInMemory = true; parameters.GenerateExecutable = false; code = string.Format(embedCode, "{", "}", functionName, code); CompilerResults results = provider.CompileAssemblyFromSource(parameters, code); if (results.Errors.HasErrors) { StringBuilder sb = new StringBuilder(); foreach (CompilerError error in results.Errors) { sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText)); } throw new InvalidOperationException(sb.ToString()); } Type binaryFunction = results.CompiledAssembly.GetType(string.Format("MagicCSIPython.MagicCSFunctions_{0}", functionName)); return binaryFunction.GetMethod(functionName); } public static object RunFunction(MethodInfo function, object[] parameters) { return function.Invoke(null, parameters); } } }
The second part consists in a wrapper around this DLL
def create_cs_function(name, code, dependencies = None): AddReference("MagicIPython.dll") from MagicIPython import MagicCS from System import String from System.Collections.Generic import List if dependencies is not None and len(dependencies) > 0 : myarray = List[String]() for i,d in enumerate(dependencies): myarray.Add( d ) myarray = myarray.ToArray() else: myarray = List[String]().ToArray() obj = MagicCS.CreateFunction(name, code, myarray) return lambda *params: run_cs_function(obj, params) def run_cs_function(func, params): AddReference("MagicIPython.dll") from MagicIPython import MagicCS from System.Collections.Generic import List from System import Object par = List[Object]() for p in params : par.Add ( p ) return MagicCS.RunFunction(func, par.ToArray())
And the last one is the magic command itself:
import sys from IPython.core.magic import Magics, magics_class, line_magic, cell_magic from IPython.core.magic import line_cell_magic from IPython.core.display import HTML @magics_class class CustomMagics(Magics): @cell_magic def CS(self, line, cell): """ Defines command ``%%CS``. """ if not sys.platform.startswith("win"): raise Exception("Works only on Windows.") from ..tips_tricks.pythoncs import create_cs_function if line is not None: spl = line.strip().split(" ") name = spl[0] deps = " ".join(spl[1:]) if len(spl) > 1 else "" deps = deps.split(";") if name == "-h": print( "Usage: " " %%CS function_name dependency1;dependency2" " function code") else : try: f = create_cs_function(name, cell, deps) except Exception as e : print(e) return if self.shell is not None: self.shell.user_ns[name] = f return f def register_magics(): """ register magics function, can be called from a notebook """ ip = get_ipython() ip.register_magics(CustomMagics)
After that, what's left is to use it in a notebook: Python et C Sharp