IronPython 'execfile' Equivalent in C# - c#

Id like to know how to perform the functionality of the 'execfile' Python command
but from C#. In this example:
var runtime = Python.CreateRuntime();
dynamic scope = Python.UseFile("body.py");
you specify the body file.
In the header of body.py, I specify the header:
execfile('header.py')
I would like to make that execfile from C#, how can I do that?
Note: Import does not work because i'm declaring variables dynamically inside
header.py scope. import would not import that variables because they do
not exist yet.
Thank you very much!

If you want different behaviour from the exec file you can easily create your own and do things like this (share scope) by creating the function in .NET and set this to the scope. This would also allow you to add additional functionality if you wish.
This seems to work:
Setting up the engine:
var py = Python.CreateEngine();
var source = py.CreateScriptSourceFromFile(#"..\..\IronPython\body.py");
var scope = py.CreateScope();
Then I create the function that can be used by scripts and set it on the scope
Action<string> execFileCallback = fileName =>
{
var s2 = py.CreateScriptSourceFromFile(fileName);
s2.Execute(scope);
};
((dynamic)scope).myexecfile = execFileCallback;
source.Execute(scope);
Now my Body.py looks like this:
someVarSetByBody = "This was set by the body"
Console.WriteLine("Body is loading")
myexecfile(r"..\..\IronPython\Header.py")
Console.WriteLine("Body has loaded")
And my Header.py Looks like this:
Console.WriteLine("Header is loading")
Console.WriteLine("Variable set by body: %s" % someVarSetByBody)
Console.WriteLine("Header has loaded")
It is now able to use the same scope in the different scripts so you can share the variables.

Related

Trying to use a variable as Hyperlink markdown in Discord

actually Im working on an Upload-Bot for Discord. My problem is I wanna use a variable (that contains a api permalink) as a Hyperlink markdown.
At the moment it looks like this:
But it should look like this: (The "Vale Guardian" Hyperlink mardown should contain the permalink from the "DpsReportVg" variable)
using (WebClient client2 = new WebClient())
{
DpsReport1 = client2.DownloadString("https://dps.report/getUploads?json=1&userToken=5656165565161312564651635");
}
var dataObject = JsonConvert.DeserializeObject<dynamic>(DpsReport1);
string DpsReportVg = dataObject.uploads[3].permalink.ToString();
var embed = new EmbedBuilder();
embed.WithTitle("DPS-Reports uploaded by ");
embed.WithDescription(Context.User.Username);
embed.WithColor(new Color(0, 255, 0));
embed.WithCurrentTimestamp();
embed.AddField("Spirit Vale", "[Vale Guardian](DpsReportVg)");
You almost had it, but you're overlooking a minor detail.
You have
embed.AddField("Spirit Vale", "[Vale Guardian](DpsReportVg)");
But what you should have is
embed.AddField("Spirit Vale", $"[Vale Guardian]({DpsReportVg})");
Explanation:
You aren't actually using your variable, you are just adding a String that happens to match your variable name.
My edit to your code uses string interpolation to insert your variable into your string thereby providing the actual link that you have stored in the variable.
You need to use the EmbedBuilder.withUrl method:
Check out the official docs for a complete example with images:
https://discord4j.readthedocs.io/en/latest/Making-embedded-content-using-EmbedBuilder/

Why do I get Microsoft.Scripting.SyntaxErrorException: unexpected token ',' when importing libraries using IronPython

I'm using IronPython v 2.7.8.1 in VS 2017. I've installed Python27, 36 and 37. I've tried switching between the various environments in VS. I've tried adding the search paths to the libraries of these installs. The python code will work if I run it in the interpreter. Trying to run the same code in VS throws: "Microsoft.Scripting.SyntaxErrorException: unexpected token ',' ". If I test a python script that doesn't include imports it will work? Is there a specific way python has to be installed to work IronPython? This is the C# Code:
class CallPython
{
public void PatchParameter(string parameter)
{
var FilePath = (#"C:\Users\Pac\Downloads\MvcAuth\MvcAuth\TwurlPy\TwurlPy\SendDirectMsg.py");
var engine = Python.CreateEngine(); // Extract Python language engine from their grasp
ICollection<string> searchPaths = engine.GetSearchPaths();
searchPaths.Add(#"C:\Users\Pac\AppData\Local\Programs\Python\Python37\Lib");
searchPaths.Add(#"C:\Users\Pac\AppData\Local\Programs\Python\Python37\Lib\site-packages");
engine.SetSearchPaths(searchPaths);
var scope = engine.CreateScope(); // Introduce Python namespace
(scope)
var d = new Dictionary<string, object>
{
{ "text", text},
{ "userID", userID},
};
// Add some sample parameters. Notice that there is no need in
// specifically setting the object type, interpreter will do that part for us
// in the script properly with high probability
scope.SetVariable("params", d); // This will be the name of the
// dictionary in python script, initialized with previously created .NET
// Dictionary
ScriptSource source = engine.CreateScriptSourceFromFile(FilePath);
// Load the script
object result = source.Execute(scope);
parameter = scope.GetVariable<string>("parameter"); // To get the
// finally set variable 'parameter' from the python script
return;
}
}
This is the Python script. If I comment out the import statements it works using IronPython, but of course I need them...
import twitter
import requests
import sys
parameter = "test"
def SendDM(text, userID, access_token, access_secret):
consumer_key = 'YkopsCQjEXccccccccccccccccccZvA9yy'
consumer_secret = 'TQVCoccccccccccccccccccct7y8VfmE'
access_token_key = access_token
access_token_secret = access_secret
api = twitter.Api(
consumer_key=consumer_key,
consumer_secret=consumer_secret,
access_token_key=access_token_key,
access_token_secret=access_token_secret)
send_msg = api.PostDirectMessage(text, user_id=userID)
print (send_msg)
return
SendDM(text, userID, access_token, access_secret)

C# and IronPython integration

I want simply use io.py from C# to write a file and I use the following code:
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
...
System.IO.Directory.SetCurrentDirectory(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) +
"\IronPython\Lib");
ScriptRuntime py = Python.CreateRuntime();
dynamic io = py.UseFile("io.py");
dynamic f = io.open("tmp.txt", "w");
f.writelines("some text...");
f.close();
but when I run the program the runtime give me a:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException telling that no overload of writelines accept argument '1'
it seems like that method doesn't exists... but into io.py documentation it exists.
P.S. The same is for close method!!!
Any idea?
I can only tell you how to make your code working, however I don't have much experience with IronPython and have no idea why it is done this way (though I try to learn that). First, it seems io module is treated in a special way and there is special (non-dynamic) class for that. When you do io.open what is returned is instance of PythonIOModule._IOBase class. You can do
var f = (PythonIOModule._IOBase) io.open("tmp.txt", "w");
And see for yourself that "writeline" method (which is regular method, not a dynamic one) accepts CodeContext instance as first argument, and second argument is lines. Interesting that this class itself already contains field with that CodeContext, but it is made internal for some reason, and what is even worse - writelines (and other methods) could have been using that CodeContext and not require us to provide external one. Why is it done like this - I have no idea.
So to make your code working, we have to get CodeContext somewhere. One way is do that via reflection:
var context = (CodeContext) f.GetType().GetField("context", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(f);
Another way is to craft it yourself:
var languageContext = HostingHelpers.GetLanguageContext(engine);
var context = new ModuleContext(io._io, new PythonContext(languageContext.DomainManager, new Dictionary<string, object>())).GlobalContext;
Both methods will work and program will successfully write to a file. Full working sample:
static void Main(string[] args) {
System.IO.Directory.SetCurrentDirectory(#"G:\Python27\Lib");
var engine = Python.CreateEngine();
dynamic io = engine.ImportModule("io");
var f = (PythonIOModule._IOBase) io.open("tmp.txt", "w");
var languageContext = HostingHelpers.GetLanguageContext(engine);
var context = new ModuleContext(io._io, new PythonContext(languageContext.DomainManager, new Dictionary<string, object>())).GlobalContext;
f.writelines(context, "some text....");
f.close(context);
}

instantiate python class into C# using the class name as string

This SO question provides code to create an instance of a python class in C#.
The following code forces to know the python function name in advance. However I need to specify the class name and the function name to be executed by strings.
ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();
source.Execute(scope);
dynamic class_object = scope.GetVariable("Calculator");
dynamic class_instance = class_object();
int result = class_instance.add(4, 5); // I need to call the function by a string
Easiest way to do that is to install nuget package called Dynamitey. It was designed specifically to call dynamic methods (and doing other useful things) on dynamic objects. After you install it, just do:
static void Main(string[] args)
{
ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();
source.Execute(scope);
dynamic class_object = scope.GetVariable("Calculator");
dynamic class_instance = class_object();
int result = Dynamic.InvokeMember(class_instance, "add", 4, 5);
}
If you want to know what it does under the hood - it uses the same code which is used by C# compiler for dynamic invocations. This is a long story but if you want to read about this, you can do it here for example.
You're looking for the Invoke and InvokeMember IronPython methods:
ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();
source.Execute(scope);
object class_object = scope.GetVariable("Calculator");
object class_instance = engine.Operations.Invoke(class_object);
object[] args = new object[2];
args[0] = 4;
args[1] = 5;
int result = (int)engine.Operations.InvokeMember(class_instance, "add", args); // Method called by string
// "args" is optional for methods which don't require arguments.
I also changed the dynamic type to object, since you won't need it anymore for this code sample, but you are free to keep it, should you need to call some fixed-name methods.

Adding result set to SSIS SQL task with Executable

How can an instance of ExecuteSQLTask (Microsoft.SqlServer.SQLTask) be added to a DtsEventHandler?
Building the ExecuteSQLTask is straight forward, but adding it to an event handler may require some kind of casting.
I prefer not creating it this way, because I cannot add parameter mapping to the result set this way. Unless there is a way to add parameters to the Executable below?
DtsEventHandler ehOnError = (DtsEventHandler)package.EventHandlers.Add("OnError");
Executable execOnError = ehOnError.Executables.Add("STOCK:SQLTask");
TaskHost thOnError = (TaskHost)execOnError;
thOnError.Name = "sql_Exec_LogMessage_Error";
thOnError.SetExpression("SqlStatementSource", "#[User::sql_LogMessageError]");
thOnError.Properties["Connection"].SetValue(thOnError, cmag.ID);
// Parameter mapping?
Don't you just need the ExecuteSQLTask now, and then follow their Parameter Binding example?
Executable execOnError = ehOnError.Executables.Add("STOCK:SQLTask");
TaskHost thOnError = (TaskHost)execOnError;
thOnError.Name = "sql_Exec_LogMessage_Error";
thOnError.SetExpression("SqlStatementSource", "#[User::sql_LogMessageError]");
thOnError.Properties["Connection"].SetValue(thOnError, cmag.ID);
ExecuteSQLTask task = thOnError.InnerObject as ExecuteSQLTask;
...
task.ParameterBindings.Add();
IDTSParameterBinding parameterBinding = task.ParameterBindings.GetBinding(0);
parameterBinding.DtsVariableName = "User::Variable";
parameterBinding.ParameterDirection = ParameterDirections.Input;
...

Categories