Accesing script scope variables from modules - c#

We use IronPython in our open source project. I have problem accesing the variables added to the script scope like
private ScriptScope CreateScope(IDictionary<string, object> globals)
{
globals.Add("starting", true);
globals.Add("stopping", false);
var scope = Engine.CreateScope(globals);
scope.ImportModule("math");
return scope;
}
https://github.com/AndersMalmgren/FreePIE/blob/master/FreePIE.Core/ScriptEngine/Python/PythonScriptEngine.cs#L267
I can use the globals from the main script, but any module that is loaded will fail. How can it be fixed?
update: Given this module mymodule.py
if starting: #starting is defined on the scope
...
From the main script executed using this code
void RunLoop(string script, ScriptScope scope)
{
ExecuteSafe(() =>
{
var compiled = Engine.CreateScriptSourceFromString(script).Compile();
while (!stopRequested)
{
usedPlugins.ForEach(p => p.DoBeforeNextExecute());
CatchThreadAbortedException(() => compiled.Execute(scope));
scope.SetVariable("starting", false);
threadTimingFactory.Get().Wait();
}
scope.SetVariable("stopping", true);
CatchThreadAbortedException(() => compiled.Execute(scope));
});
}
https://github.com/AndersMalmgren/FreePIE/blob/master/FreePIE.Core/ScriptEngine/Python/PythonScriptEngine.cs#L163
from mymodule import * #this will load the moduel and it fails with
edit: In response to #BendEg's answer
I tried this
scope.SetVariable("__import__", new Func<CodeContext, string, PythonDictionary, PythonDictionary, PythonTuple, object>(ResolveImport));
ImportDelegate is not defined so tried using a Func instead, the ResolveImport method never triggers and I get the same exception that the name is not defined
edit: I changed the scope creation to
var scope = Engine.GetBuiltinModule();
globals.ForEach(g => scope.SetVariable(g.Key, g.Value));
Now the import delegate triggers but it crashes on first line with global name 'mouse' is not defined, mouse is not used from the module. It seems its confused when I add my custom globals to the BuiltinModule

This is probably not the right answer, as still doesn't allow to share variables defined in the main IronPhyton script with the imported modules, however is a step forward.
This approach allows to set variables at the Engine level rather then the script level and they will be available in every imported module.
engine = Python.CreateEngine();
engine.Runtime.Globals.SetVariable("test_global", "This is a test global variable.");
then, within any IronPhyton script is possible to access it using an import:
import test_global
print(test_global)
Unlike ScriptScope, which makes them directly available, those Global variables need to be imported.
Original article
https://ludovic.chabant.com/devblog/2009/12/24/exposing-global-variables-in-ironpython/
Disclaimer
I added this answer as I've been struggling to find any material on this topic other then this SO Q&A, so I'm posting this possible workaround to help future troubled readers (like myself)

As far as i know, importing some module will create a new scope. So when creating an instance of PythonModule via from ... import ... they has it's own scope. In this new scope, your public variables are not available. Please correct me if i am wrong.
Workaround:
You could create some static class, which holdes the values. Than you can be sure, you always have them. For example:
namespace someNS
{
public static class SomeClass
{
public static bool Start { get; set; }
}
}
And than in your IP-Code:
from someNS import SomeClass
# Now you can access the member
yourVal = SomeClass.Start
maybe this is some thing you can use. You event don't need to set it as variable in the scope.
EDIT
Maybe this is working for you. In the code i override module importing and try to set the global vars:
First thing you need is, give IronPython some delegate, for module importing:
# Scope should be your default scope
scope.SetVariable("__import__", new ImportDelegate(ResolveImport));
Then override the import function:
private object ResolveImport(CodeContext context, string moduleName, PythonDictionary globals, PythonDictionary locals, PythonTuple fromlist)
{
// Do default import but set module
var builtin = IronPython.Modules.Builtin.__import__(context, moduleName, globals, locals, fromlist, 0);
context.ModuleContext.Module.__setattr__(context, "some_global", "Hello World");
return builtin;
}
EDIT
Definition of ImportDelegate
delegate object ImportDelegate(CodeContext context, string moduleName, PythonDictionary globals, PythonDictionary locals, PythonTuple fromlist);

Related

Blazor - How to Call Independent JSInterop Methods Located on Layout and Child Components

Up until the day before this post was first made, I have been calling JSInvokable methods just fine from any page loaded in the app. I was creating and establishing the the DotNotObjectReference objects as traditionally shown in most examples like so:
var dotNetReference = DotNetObjectReference.Create(this);
JSRuntime.InvokeVoidAsync("SetDotNetObjectRef", dotNetReference);
In the individual pages themselves, I would create the invokable methods like so:
#page "/Application/Search/Index/"
#code {
[JSInvokable]
public void InvokableMethodOnIndexPage(string id)
{
return;
}
protected override void OnAfterRender(bool isFirstRender)
{
if (isFirstRender)
{
var dotNetReference = DotNetObjectReference.Create(this);
JSRuntime.InvokeVoidAsync("SetDotNetObjectRef", dotNetReference);
}
}
}
Then, I needed to build a new JSInvokable method that was to be re-used on any page in the app, and so I placed that method in my "MainLayout.razor" component that all of my pages derive the design layout from like so:
#code {
[JSInvokable]
public void InvokableMethodOnMainLayout(string id)
{
return;
}
protected override void OnAfterRender(bool isFirstRender)
{
if (isFirstRender)
{
var dotNetReference = DotNetObjectReference.Create(this);
JSRuntime.InvokeVoidAsync("SetDotNetObjectRef", dotNetReference);
}
}
}
In my first attempts to call the new function that I placed in the "MainLayout.razor" file, I was getting the "The type 'Index' does not contain a public invokable method with [JSInvokableAttribute('InvokableMethodOnMainLayout')].".
Through my research and testing, I discovered that both of the DotNetObjectReference.Create(this) lines in each file were clashing with each other with child instance overriding the "MainLayout" DotNetObjectReference instance. When I removed the child instance reference, the call to the method in MainLayout would execute fine but then in turn, it broke the ability to the call the InvokableMethodOnIndexPage(string id) method.
My first thought was to alter the _Host view file to maintain an array of references and then build a helper function that would loop through the references and try/catch each reference until it hit a reference that it could find the function I wanted to call but I decided that may not be the best approach and/or would be expensive operation.
Therefore, may I ask what the appropriate implementation here to provide the functionality to call JSInvokable methods from any component?
Regards.
So, after having a revelation and giving this some more thought, I tested a different way to address the original clashing of DotNetObjectReference between parent and child Blazor components and came up with the following code to address the issue at hand.
The first thing was to redo/create JavaScript helper functions inside of the _Host file to resemble the following:
window.DotNetObjectRefs = [];
window.SetDotNetObjectRef = function (dotNetObjectRef, componentName) {
if(window.DotNetObjectRefs.filter(dnor => return dnor.ComponentName === componentName).length <= 0)
{
window.DotNetObjectRefs.push({ ComponentName: componentName, Reference: dotNetObjectRef });
}
};
window.InvokeMethodAsync = function (assembly, componentName, method, args) {
for(let r = 0; r < window.DotNetObjectRefs.length; r++)
{
if (window.DotNetObjectRefs[r].ComponentName === componentName)
{
window.DotNetObjectRefs[r].Reference.invokeMethodAsync(method, args);
}
}
};
The key change to this was to hold an array of references that don't clash with each other. By creating an array to hold references, you can create multiple references and add them with a naming convention that refers to the component the reference was instantiated from. Then, when you need to call an invokable function, you just specify the component the invokable method comes from in addition to the method name and arguments.
Then, in any component that requires JSInterop capabilities, this:
JSRuntime.InvokeVoidAsync("SetDotNetObjectRef", DotNetObjectReference.Create(this));
becomes this:
JSRuntime.InvokeVoidAsync("SetDotNetObjectRef", DotNetObjectReference.Create(this), "Reference_Component_Name");
This will now allow you to define JSInvokable methods on any component and have them be called from any other component as long as you reference the component name in the Invoke calls.

Variable scope abstraction

I need 'static' variable that scope is per query, not per application. I describe it a little bit more below so:
I've got some process that evaluates queries. Each query requires it's own ID and I can process multiple queries in parallel. It basically looks like:
OnServiceStart(){
var env = new Environement();
env.SetValue(...);
...
}
ProcessQuery(string query){
var id = Guid.NewId();
var evaluator = CreateEvaluatorBasedOnQuery();
evaluator.Evaluate();
}
I also have very simple class that holds my environment variables:
public class Environment
{
private static readonly ConcurrentDictionary<string, object> Objects;
static Environment()
{
Objects = new ConcurrentDictionary<string, object>();
}
public T Value<T>(string name)
{
return (T) Objects[name];
}
public void SetValue<T>(string name, T value)
{
if (!Objects.TryAdd(name, value))
Objects.TryUpdate(name, value, Objects[name]);
}
...
}
Environment is set up with values like:
env.SetValue(EnvironmentServiceHelper.PluginsFolderKey, ApplicationConfiguration.PluginsFolder);
env.SetValue(EnvironmentServiceHelper.HttpServerAddressKey, ApplicationConfiguration.HttpServerAdress);
env.SetValue(EnvironmentServiceHelper.ServerAddressKey, ApplicationConfiguration.ServerAddress);
env.SetValue(EnvironmentServiceHelper.TempFolderKey, Path.GetTempPath());
However EnvironmentServiceHelper.TempFolderKey needs to be slightly different per query so instead Path.GetTempPath() I would like it to be $"{Path.GetTempPath()}\{id}".
My requirement is that it should be completelly transparent for evaluator classes. I can't pass id so code like that is not valid.
Environment env = new Environemnt();
env.GetValue(...)
evaluator.Evaluate(id);
GetValue(...) must returns $"{Path.GetTempPath()}\{id}" where id is different for each query.
Can't get it how to build appropriate abstraction to approach this. Currently, I would like to avoid achieving it by separating with different AppDomain. Could someone hints me something?
Why not create a layered Environment? The inside environment keeps the global properties, the outside environment the query specific ones? This object is passed along to every method requiring it.
You start of with a constructor like this:
Environment env = new Environment(originalEnvironment);
Depending on your wishes, you can copy all values from the originalEnvironment to the newly instantiated Environment, or keep a reference.

translating to c# code the use of a class method trough an object variable

I have just two years I started programming (mostly C#), but there's still a lot I donĀ“t know about the language, I'm trying to translate a web project from VB.NET code to C# code, but I got to a section which I don't know how to translate.
The class in the VB code uses a sub procedure to do an INSERT to a database, to do this, in the class there is a db variable declared as an Object, then the db variable uses a function from a module to change the class type depending on the conection type and database is going to be used (this is determined from getting a value from the Web.config file), then after building the query, the db variable uses another function from the new class type assigned to execute the query.
To better ilustrate, here is the VB code.
This is the part where the db variable is used:
Public class MyClass1
Private Sub _AddDB()
Dim db As Object
Dim query As String
db = TypeDB()
query = "Some Query"
db.ExecuteDB(query)
End Sub
End Class
As I said, the TypeDB() function is in a Module:
Module MyModule
Public Function TypeDB() As Object
Dim Str As String = ConfigurationSettings.AppSettings.GetValues("strConnDB").GetValue(0)
Dim db As Object
Select Case Str
Case "SQL"
db = New DBSql_Class
Case "ODBC"
db = New DBOdbc_Class
Case "OLE"
db = New DBOle_Class
End Select
TypeDB = db
End Function
End Module
Each of the classes mentioned on the module has its own ExecuteDB function so there is no problems in executing the line db.ExecuteDB(query). I read that the closest thing to a Module in C# is a static class, so that was what I did with MyModule, I already have the rest of the classes, but when I try:
public class MyClass1
{
private void _AddDB()
{
object db;
string query;
db = MyModuleClass.TypeDB();
query = "Some Query";
db.ExecuteDB(query);
}
}
I immediately get the error that the db objetc does not contain a definition for the method ExecuteDB.
My first idea was to do a switch statement inside the _AddDB method, but it will not be optimal, because there is already a switch statement in the MyModuleClass to select the new type of class for the db variable, I tried searching for a solution, but all I found was only solutions that only uses one class.
I want to do this as close to the original project as posible, but this kind of interaction is all over the VB project.
Object-oriented clean solution to this problem in C# is to define an interface with ExecuteDB() operation and implement it in those three DB classes. Then return that interface from your static method, and then you can call it directly.
interface IDatabase {
object ExecuteDB(string);
}
class DBSql_Class : IDatabase {
public object ExecuteDB(string query) {
...
}
}
static class MyModuleClass {
public static IDatabase TypeDB() {
switch(...) {
...
return new DBSql_Class();
...
}
}
}
Just a note: your class names are terrible. Please try to follow standard naming scheme when programming in .NET. :-)
You need to declare 'db' as dynamic in C# to achieve the same sort of late-binding that the VB code is doing.
e.g.,
dynamic db = null;
string query = null;
db = MyModule.TypeDB();
query = "Some Query";
db.ExecuteDB(query);
Or, better yet, use the appropriate types instead of 'object'.

How to get all visible local variable names within a scope with Roslyn (Microsoft CodeAnalysis)

(Please note: This is not about run-time reflection/metainfo)
I am writing a concrete implementation of Roslyn CSharpSyntaxVisitor
When implementing the VisitIdentifierName
public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax name)
{
var symbolInfo = _semanticModel.GetSymbolInfo(name);
var fieldSymbol = symbolInfo.Symbol as IFieldSymbol;
if (fieldSymbol != null)
{
// Here I would like to get all the local variable names what are visible
// in the very same scope where this field IdentifierNameSyntax under visiting resides
// We can suppose that _semanticNodel for the Document is available.
}
}
Call SemanticModel.LookupSymbols() (source), then filter for local variables.
You may also want to filter out locals declared after that location; see this code.

set filepath from database in SSIS variable for logging

I have some paths in my database table like this:
ID PATHXML PATHPICT PATHSONG
1 D:\XML\Here D:\Picture\Image D:\Blur\Song2
2 D:\XML\File D:\Picture\X-Files D:\IRONMAIDEN\Fearofthedark
I want to switch on logging in SSIS and save this log result to PathSong eg. D:\Blur\Song2\eventlog.log
I created a variable to get this path. But when I create an expression using this variable, it doesn't work. So what can I do to fix this?
Accessing package variables in a Script Component (of a Data Flow Task) is not the same as accessing package variables in a Script Task. For a Script Component, you first need to open the Script Transformation Editor (right-click on the component and select "Edit..."). In the Custom Properties section of the Script tab, you can enter (or select) the properties you want to make available to the script, either on a read-only or read-write basis:
Then, within the script itself, the variables will be available as strongly-typed properties of the Variables object:
// Modify as necessary
public override void PreExecute()
{
base.PreExecute();
string thePath = Variables.FilePath;
// Do something ...
}
public override void PostExecute()
{
base.PostExecute();
string theNewValue = "";
// Do something to figure out the new value...
Variables.FilePath = theNewValue;
}
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
string thePath = Variables.FilePath;
// Do whatever needs doing here ...
}
One important caveat: if you need to write to a package variable, you can only do so in the PostExecute() method.
Regarding the code snippet:
IDTSVariables100 varCollection = null;
this.VariableDispenser.LockForRead("User::FilePath");
string XlsFile;
XlsFile = varCollection["User::FilePath"].Value.ToString();
varCollection is initialized to null and never set to a valid value. Thus, any attempt to dereference it will fail.

Categories