I am trying to call a python script (SamplePython.py) from a C# console application using IronPython. When I run my console application, the script executes successfully and results are as expected. However I am not sure how I can add intellisense and auto complete for the custom .net types used in my script.
I tried importing python clr and calling clr.AddReference("customdll") but it did not provide any sought of intellisense.
Code Snippet of C# console code
ScriptSource pythonScript;
ObjectOperations objectOperations;
ScriptScope scope;
ScriptEngine pythonEngine = Python.CreateEngine();
scope = pythonEngine.CreateScope();
objectOperations = pythonEngine.Operations;
pythonScript = pythonEngine.CreateScriptSourceFromFile(#"<Path to script File>/SamplePython.py");
pythonScript.Execute(scope);
dynamic pythonMethod = scope.GetVariable("PythonMethod");
PythonTestType pythonTestType = new PythonTestType();
pythonTestType.TestProp = 8;
int x = pythonMethod(pythonTestType);
Code Snippet of C# custom type.
namespace PythonTestLibrary
{
public class PythonTestType
{
public int TestProp { get; set; }
}
}
Code Snippet of python script.
def PythonMethod(pythonTestType):
return pythonTestType.TestProp
When writing my python script in visual studio code, I expect the IDE to list out "TestProp" when I type pythonTestType and then the dot operator as part of intellisense just like I get for other python inbuilt datatypes.
Related
I have an application that exposes some c# objects to an embedded IronPython interpreter like this:
using IronPython.Hosting;
namespace ironpy
{
public class Foo
{
public void bar(string message)
{
System.Console.WriteLine(message);
}
}
class Program
{
static void Main(string[] args)
{
var engine = Python.CreateEngine();
var scope = engine.CreateScope();
var test = new Foo();
scope.SetVariable("foo", test);
string code = "foo.bar('Hello')";
engine.Execute(code, scope);
}
}
}
In practice the python source code comes from an textfile loaded at runtime.
When editing such a source file in VSCode or other editors the language server that provides code completion obviously doesn't have any idea about whats going to be available. So you have no code completion and annoying error squiggles everywhere.
Is it somehow possible to write some sort of plugin/hint/linting file for one of the commonly used language servers to tell them about the exported C# API to get working code completion when editing the python source files?
I found the solution to my problem in the meantime: IronPython Stubs files
https://github.com/gtalarico/ironpython-stubs
In my opinion, defining .py file and implementing codes inside on it is the best way. Then you can execute your .py file like below;
https://stackoverflow.com/a/11779234/4582992
I am planning to use IronPython along with C# for following use case.
I want to expose some functions which can invoked via python script.
Such script will be invoked by C# application.
For example there could be function prepared in C# ClearUserInput() that clears some data on WPF. Then I could expose more functions via some interface.
public interface IScriptContract
{
void ClearMainWindow();
void LoadProject(string projectName);
}
python script:
print "clearing window"
ClearMainWindow() --how to cooperating with C# code?
print "load proj"
ClearMainWindow("project.proj") --how to cooperating with C# code?
I want user can write some script that can invoke my .net function.
Is it possible to do that?
The standard way to do this is to create a 'host object' that is passed into the scripts that provides access to the host application (if you're familiar with web progamming, that's what the window instance is).
In IronPython you can use SetVariable on the ScriptScope object that is executing your code:
var contract = new ScriptContract();
var engine = Python.CreateEngine();
var mainScope = engine.CreateScope();
var scriptSource = engine.CreateScriptSourceFromFile("test.py", Encoding.Default, SourceCodeKind.File)
mainScope.SetVariable("host", contract);
scriptSource.Execute(scope);
From python it's then always available:
host.ClearMainWindow()
I'm using Visual Studio 2010. I have an IronPython console project and a C# console project. This IronPython script works fine when I run it by itself:
import nltk
def Simple():
baconIpsumFile = open('baconipsum.txt', 'r')
baconIpsumCorpus = baconIpsumFile.read()
tokens = nltk.word_tokenize(baconIpsumCorpus)
text = nltk.Text(tokens)
print text
Here is the C# console program, which does not work fine:
using IronPython.Hosting;
namespace IronNLTK.CSharp.Console
{
class Program
{
static void Main(string[] args)
{
var ipy = Python.CreateRuntime();
dynamic test = ipy.UseFile("C:\\Path\\To\\Program.py");
test.Simple();
}
}
}
I get an ImportException: No module named nltk. What am I missing?
sounds like you need to update sys.path to point to wherever NLTK lives.
check this out: Importing external module in IronPython
Awesome news, Visual Studio 2017 is embedded with Anaconda’s Python distribution which has NTLK and other machine learning packages.
Is there any way to make this scenario work?
There is a Python script. It is built into a DLL by running this script with IronPython:
import clr
clr.CompileModules("CompiledScript.dll", "script.py")
The goal is to call this DLL's methods from C# code. .NET Reflector shows there is one class in the DLL - DLRCashedCode and the methods we are interested in are private static methods of this class.
For example, there is a function in the script:
def scriptMethod(self, text):
...
Its representation in the DLL is:
private static object scriptMethod(Closure closure1, PythonFunction $function, object self, object text)
{
...
}
Closure and PythonFunction are IronPython classes (from Microsoft.Scripting.dll and IronPython.dll).
So far so good. Is it possible this method to be called by C# code? The idea of using reflection like
Type t = typeof(DLRCachedCode);
string methodName = "scriptMethod";
MethodInfo method = t.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);
object[] parameters = new object[] { "param1", "param2" }; // the "params problem"
method.Invoke(null, parameters);
seems harder because of setting the method's parameters. If they are (any how) initialized correctly, could we expect the method to work smoothly?
Is there a better way to call this methods from C#? For various different reasons we prefer to have the script built as a .NET assembly and not to call the script itself.
Sort of. You cannot access the Python methods directly from C# code. Unless you are playing with C# 4.0 and the dynamic keyword or you are very, very special ;). However, you can compile an IronPython class to a DLL and then use IronPython hosting in C# to access the methods (this is for IronPython 2.6 and .NET 2.0).
Create a C# program like this:
using System;
using System.IO;
using System.Reflection;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
// we get access to Action and Func on .Net 2.0 through Microsoft.Scripting.Utils
using Microsoft.Scripting.Utils;
namespace TestCallIronPython
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
ScriptEngine pyEngine = Python.CreateEngine();
Assembly myclass = Assembly.LoadFile(Path.GetFullPath("MyClass.dll"));
pyEngine.Runtime.LoadAssembly(myclass);
ScriptScope pyScope = pyEngine.Runtime.ImportModule("MyClass");
// Get the Python Class
object MyClass = pyEngine.Operations.Invoke(pyScope.GetVariable("MyClass"));
// Invoke a method of the class
pyEngine.Operations.InvokeMember(MyClass, "somemethod", new object[0]);
// create a callable function to 'somemethod'
Action SomeMethod2 = pyEngine.Operations.GetMember<Action>(MyClass, "somemethod");
SomeMethod2();
// create a callable function to 'isodd'
Func<int, bool> IsOdd = pyEngine.Operations.GetMember<Func<int, bool>>(MyClass, "isodd");
Console.WriteLine(IsOdd(1).ToString());
Console.WriteLine(IsOdd(2).ToString());
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
Make a trivial Python class like this:
class MyClass:
def __init__(self):
print "I'm in a compiled class (I hope)"
def somemethod(self):
print "in some method"
def isodd(self, n):
return 1 == n % 2
Compile it (I use SharpDevelop) but the clr.CompileModules method should also work. Then shove the compiled MyClass.dll into the directory where the compiled C# program lives and run it. You should get this as the result:
Hello World!
I'm in a compiled class (I hope)
in some method
in some method
True
False
Press any key to continue . . .
This incorporates Jeff's more direct solution that eliminates having to create and compile a small Python 'stub' and also shows how you can create C# function calls that access the methods in the Python class.
The clr.CompileModules is purely a load-time optimization - it doesn't make the scripts directly available to a static languge like C#. You'll need to host the IronPython runtime, and then you can load the DLL into the runtime and use IronPython's hosting interfaces to access it.
I've written a class in python that I want to wrap into a .net assembly via IronPython and instantiate in a C# application. I've migrated the class to IronPython, created a library assembly and referenced it. Now, how do I actually get an instance of that class?
The class looks (partially) like this:
class PokerCard:
"A card for playing poker, immutable and unique."
def __init__(self, cardName):
The test stub I wrote in C# is:
using System;
namespace pokerapp
{
class Program
{
static void Main(string[] args)
{
var card = new PokerCard(); // I also tried new PokerCard("Ah")
Console.WriteLine(card.ToString());
Console.ReadLine();
}
}
}
What do I have to do in order to instantiate this class in C#?
IronPython classes are not .NET classes. They are instances of IronPython.Runtime.Types.PythonType which is the Python metaclass. This is because Python classes are dynamic and support addition and removal of methods at runtime, things you cannot do with .NET classes.
To use Python classes in C# you will need to use the ObjectOperations class. This class allows you to operate on python types and instances in the semantics of the language itself. e.g. it uses the magic methods when appropriate, auto-promotes integers to longs etc. You can find out more about ObjectOperations by looking at the source or using reflector.
Here is an example. Calculator.py contains a simple class:
class Calculator(object):
def add(self, a, b):
return a + b
You can use it from your pre .NET 4.0 C# code like this:
ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();
ObjectOperations op = engine.Operations;
source.Execute(scope); // class object created
object klaz = scope.GetVariable("Calculator"); // get the class object
object instance = op.Call(klaz); // create the instance
object method = op.GetMember(instance, "add"); // get a method
int result = (int)op.Call(method, 4, 5); // call method and get result (9)
You will need to reference the assemblies IronPython.dll, Microsoft.Scripting and Microsoft.Scripting.Core.
C# 4 made this much easier with the new dynamic type.
ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();
source.Execute(scope);
dynamic Calculator = scope.GetVariable("Calculator");
dynamic calc = Calculator();
int result = calc.add(4, 5);
If you are using Visual Studio 2010 or later with NuGet support simply execute this to download and reference the appropriate libraries.
Install-Package IronPython
Now that .Net 4.0 is released and has the dynamic type, this example should be updated. Using the same python file as in m-sharp's original answer:
class Calculator(object):
def add(self, a, b):
return a + b
Here is how you would call it using .Net 4.0:
string scriptPath = "Calculator.py";
ScriptEngine engine = Python.CreateEngine();
engine.SetSearchPaths(new string[] {"Path to your lib's here. EG:", "C:\\Program Files (x86)\\IronPython 2.7.1\\Lib"});
ScriptSource source = engine.CreateScriptSourceFromFile(scriptPath);
ScriptScope scope = engine.CreateScope();
ObjectOperations op = engine.Operations;
source.Execute(scope);
dynamic Calculator = scope.GetVariable("Calculator");
dynamic calc = Calculator();
return calc.add(x,y);
Again, you need to add references to IronPython.dll and Microsoft.Scripting.
As you can see, the initial setting up and creating of the source file is the same.
But once the source is succesfully executed, working with the python functions is far easier thanks to the new "dynamic" keyword.
I am updating the above example provided by Clever Human for compiled IronPython classes (dll) instead of IronPython source code in a .py file.
# Compile IronPython calculator class to a dll
clr.CompileModules("calculator.dll", "calculator.py")
C# 4.0 code with the new dynamic type is as follows:
// IRONPYTHONPATH environment variable is not required. Core ironpython dll paths should be part of operating system path.
ScriptEngine pyEngine = Python.CreateEngine();
Assembly myclass = Assembly.LoadFile(Path.GetFullPath("calculator.dll"));
pyEngine.Runtime.LoadAssembly(myclass);
ScriptScope pyScope = pyEngine.Runtime.ImportModule("calculator");
dynamic Calculator = pyScope.GetVariable("Calculator");
dynamic calc = Calculator();
int result = calc.add(4, 5);
References:
Using Compiled Python Classes from .NET/CSharp IP 2.6
Static Compilation of IronPython scripts
I have searched high and low and I am afraid that there does not seem to be much information pertaining to this. I am pretty much certain that no one has devised a way to do this in the clean manner that you would like.
The main reason I think this is a problem is that in order to see the PokerCard type in your C# application you would have to compile your Python code to IL. I don't believe that there are any Python->IL compilers out there.