oleg-shilo / cs-script

C# scripting platform
http://www.cs-script.net
MIT License
1.56k stars 234 forks source link

How can behavior similar to Python's reloading be implemented? #367

Closed int2e closed 2 months ago

int2e commented 2 months ago

How can I achieve behavior similar to Python's importlib.reload() in CS-Script? This is very important. Is it currently possible to do this?

oleg-shilo commented 2 months ago

Can please you explain the desired behaviour so do not misinterpret your request?

I have a feeling that you are referring to recompiling already loaded scripts but I do not want to be guessing.

int2e commented 2 months ago

In Python, when using importlib.reload() to reload, if there is any code that has not finished executing, it will first complete the execution of the old code. The next time it executes, it will use the new code.

oleg-shilo commented 2 months ago

Not sure I follow. I still have no visibility of the problem you are trying to solve. Are you executing a script from the shell? Or you are executing it from the app where you host the script engine. . . . OK, I just googled it. importlib.reload() is user to reload previously loaded modules. In .NET the closest equivalent of module is an assembly. Thus what you are trying to solve is a pure .NET challenge that is not related to C# scripting as such.

In .NET you cannot reload a statically loaded assembly. But if your assembly is loaded dynamically then you can definitely reload it. Something like this pseudo-code (e.g. you are loading a printer assembly):


object CreatePrinter()
{
    var asmFile = "<path to the DLL>";
    Assembly asm = Assembly.Load(File.ReadAllBytes(asmFile));
    Type targetType = loadedAssembly.GetType("Printing.Printer");
    return Activator.CreateInstance(targetType);
} 

// load Printer assembly and create a Printer 
dynamic printer = CreatePrinter();

// reload Printer assembly and create a Printer 
printer = CreatePrinter();

In scripting the only thing the is going to be different is the assembly loading:

var scriptFile = "<path to the C# file>";
Assembly asm = CSScript.LoadFile(scriptFile));
int2e commented 2 months ago

My meaning is that if the script has been changed, I want it to update the cache, similar to "recompiling", but it will not interrupt the code that has not been executed yet. I am not sure how to do it.

int2e commented 2 months ago

For example, a function is halfway through execution when the script file is overwritten and the function logic is updated. At what moment does it update the cache? In a scenario with 30,000 script objects, CS-Script lacks a reload mechanism. Do I have to create a new object? Reload the script?

oleg-shilo commented 2 months ago

CS-Script always recompiles the script every time you load it if it has been changed since you loaded it last time.

Are you executing a script from the shell?

You still did not answer :)

int2e commented 2 months ago

CS-Script always recompiles the script every time you load it if it has been changed since you loaded it last time.

Are you executing a script from the shell?

You still did not answer :)

I am using the CS-Script NuGet package from a hosted process.

So, I can only use CSScript.Evaluator.LoadCode to reload the code and return a new Assembly object, is that correct?

int2e commented 2 months ago

My scenario involves a hosted process that supports around 10k or more online users, with each user's script environment being isolated. Reloading scripts will be a significant challenge.

oleg-shilo commented 2 months ago

Yes it will compile a new assembly (script assembly):

Thus once you load the assembly it's not going to change or impact the routines being executed. When you modified your script and loaded it again. Another assembly is loaded and executed independently:

image

oleg-shilo commented 2 months ago

In this example I loaded the script from the string variable but you can do the same from the file.

Also, keep in mind that loading too many assemblies does not have but may create some pressure on your process memory. In most of the cases you might be OK with that but if not then you may need to think about unloading the assemblies that are no longer needed. But only if you indeed need to do that.

oleg-shilo commented 2 months ago

In case you need my code to test:

static void TEST()
{
    var code = @"public class Script
                 {
                     public string Foo()
                     {
                         return ""value1"";
                     }
                 }";

    dynamic script = CSScript.RoslynEvaluator
                             .LoadCode(code);
    // load the script
    string result1 = script.Foo();

    // modifying code
    code = code.Replace("value1", "value2");

    // reload the script
    script = CSScript.RoslynEvaluator
                     .LoadCode(code);

    string result2 = script.Foo();
}
int2e commented 2 months ago

Thank you very much, I will close this issue.