Browse Source

Add initial Python IDE behaviour

tags/v0.1.0
NGnius 4 years ago
parent
commit
851483e310
5 changed files with 123 additions and 49 deletions
  1. +2
    -0
      .gitignore
  2. +39
    -3
      GamecraftScripting/Commands/DebugCommandEngine.cs
  3. +8
    -46
      GamecraftScripting/Commands/PythonRunnerCommandEngine.cs
  4. +73
    -0
      GamecraftScripting/Environment/PythonEnvironment.cs
  5. +1
    -0
      GamecraftScripting/GamecraftScripting.csproj

+ 2
- 0
.gitignore View File

@@ -450,3 +450,5 @@ dmypy.json
# Pyre type checker
.pyre/

# Gamecraft install folder
ref

+ 39
- 3
GamecraftScripting/Commands/DebugCommandEngine.cs View File

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using GamecraftModdingAPI.Commands;
using GamecraftModdingAPI.Utility;
using Svelto.ECS;
using uREPL;

using IronPython;

@@ -20,19 +21,54 @@ namespace GamecraftScripting.Commands

public IEntitiesDB entitiesDB { set; private get; }

private bool isEnabledIDE = false;

public void Dispose()
{
CommandRegistrationHelper.Unregister("PythonInfo");
CommandRegistrationHelper.Unregister("PythonVersion");
CommandRegistrationHelper.Unregister("PythonSearchPathes");
CommandRegistrationHelper.Unregister("ToggleIDE");
if (isEnabledIDE)
{
toggleIDE();
}
}

public void Ready()
{
CommandRegistrationHelper.Register("PythonInfo", ironPythonInfo, "Display Python debug info");
CommandRegistrationHelper.Register("PythonVersion", ironPythonVersion, "Display Python info");
CommandRegistrationHelper.Register("PythonSearchPathes", ironPythonPathes, "Display Python import search pathes");
CommandRegistrationHelper.Register("ToggleIDE", toggleIDE, "Toggle command line IDE tools");
}

private void ironPythonInfo()
private void ironPythonVersion()
{
Logging.CommandLog($"Assembly {typeof(PythonOptions).Assembly.GetName().ToString()}");
}

private void ironPythonPathes()
{
Logging.CommandLog("'"+string.Join("', '", Environment.PythonEnvironment.CurrentEngine.GetSearchPaths().ToArray())+"'");
}

private void toggleIDE()
{
uREPL.Parameters cliConfig = uREPL.Window.selected.parameters;
if (isEnabledIDE)
{
// disable
}
else
{
// enable
}
isEnabledIDE = !isEnabledIDE;
cliConfig.useMonoCompletion = isEnabledIDE;
cliConfig.useGameObjectNameCompletion = isEnabledIDE;
cliConfig.useGameObjectPathCompletion = isEnabledIDE;
cliConfig.useGlobalClassCompletion = isEnabledIDE;
string word = isEnabledIDE ? "On" : "Off";
Logging.CommandLog($"IDE {word}");
}
}
}

+ 8
- 46
GamecraftScripting/Commands/PythonRunnerCommandEngine.cs View File

@@ -1,18 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Numerics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Policy;

using GamecraftModdingAPI.Commands;
using GamecraftModdingAPI.Utility;
using Svelto.ECS;
using RobocraftX.Common;

using IronPython.Hosting;
using Microsoft.Scripting.Hosting;

using GamecraftScripting.Environment;
@@ -27,10 +20,6 @@ namespace GamecraftScripting.Commands

public IEntitiesDB entitiesDB { set; private get; }

private ScriptEngine pyEngine;

private ScriptScope gameScope;

public void Dispose()
{
CommandRegistrationHelper.Unregister("RunPythonScript");
@@ -42,14 +31,11 @@ namespace GamecraftScripting.Commands
CommandRegistrationHelper.Register<string>("RunPythonScript", RunPythonScript, "Run a python script stored on your computer");
CommandRegistrationHelper.Register<string>("RunPython", RunPythonString, "Run a python argument");
// create insecure Python namespace
CreateFreeInstance();
// make "import GamecraftModdingAPI" work without the clr boilerplate
pyEngine.Execute("import clr\nclr.AddReference(\"GamecraftModdingAPI\")", gameScope);
ICollection<string> searchPaths = pyEngine.GetSearchPaths();
searchPaths.Add(@".\Lib\");
searchPaths.Add(@".\Gamecraft_Data\Managed\Lib\");
pyEngine.SetSearchPaths(searchPaths);
Logging.MetaLog(string.Join(", ", pyEngine.GetSearchPaths().ToArray()));
ScriptEngine pyEngine = PythonEnvironment.CreateEngine();
ScriptScope pyScope = PythonEnvironment.CreateScope(pyEngine);
PythonEnvironment.CurrentEngine = pyEngine;
PythonEnvironment.CurrentScope = pyScope;
//Logging.MetaLog(string.Join(", ", pyEngine.GetSearchPaths().ToArray()));
}

private void RunPythonScript(string scriptPath)
@@ -65,10 +51,10 @@ namespace GamecraftScripting.Commands

private void ExecuteScript(string script, bool logError=true)
{
ScriptSource src = pyEngine.CreateScriptSourceFromString(script);
ScriptSource src = PythonEnvironment.CurrentEngine.CreateScriptSourceFromString(script);
try
{
src.Execute(gameScope);
src.Execute(PythonEnvironment.CurrentScope);
}
catch (Exception e)
{
@@ -84,33 +70,9 @@ namespace GamecraftScripting.Commands
}
}

/// <summary>
/// [Experimental] Create a Python instance without access to any(?) assemblies
/// </summary>
private void CreateSandboxedInstance()
{
Evidence evidence = new Evidence(); // an uninformative class name to limit accessible Assemblies
AppDomain domain = AppDomain.CreateDomain(GameMode.SaveGameDetails.Name, evidence);
this.pyEngine = Python.CreateEngine(domain);
PythonLogStream pyLog = new PythonLogStream();
pyEngine.Runtime.IO.SetOutput(pyLog, pyLog.Encoding);
this.gameScope = pyEngine.CreateScope();
}

/// <summary>
/// Create an unsecured Python instance
/// </summary>
private void CreateFreeInstance()
{
this.pyEngine = Python.CreateEngine();
PythonLogStream pyLog = new PythonLogStream();
pyEngine.Runtime.IO.SetOutput(pyLog, pyLog.Encoding);
this.gameScope = pyEngine.CreateScope();
}

public PythonRunnerCommandEngine()
{
Logging.CommandLog($"cwd: {Directory.GetCurrentDirectory()}");
//Logging.CommandLog($"cwd: {Directory.GetCurrentDirectory()}");
}
}
}

+ 73
- 0
GamecraftScripting/Environment/PythonEnvironment.cs View File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Security.Policy;

using RobocraftX.Common;

using IronPython.Hosting;
using Microsoft.Scripting.Hosting;

namespace GamecraftScripting.Environment
{
/// <summary>
/// Python environment factories and utilities
/// </summary>
public static class PythonEnvironment
{
public static ScriptEngine CurrentEngine { get; set; }

public static ScriptScope CurrentScope { get; set; }

/// <summary>
/// Creates and configure the Python execution engine
/// </summary>
/// <returns>The Python engine</returns>
public static ScriptEngine CreateEngine()
{
ScriptEngine pyEngine = Python.CreateEngine();
PythonLogStream pyLog = new PythonLogStream();
pyEngine.Runtime.IO.SetOutput(pyLog, pyLog.Encoding);
ICollection<string> searchPaths = pyEngine.GetSearchPaths();
// Add Python standard lib to import search pathes
searchPaths.Add(@".\Lib\"); // try not to use this location
searchPaths.Add(@".\Gamecraft_Data\Managed\Lib\");
pyEngine.SetSearchPaths(searchPaths);
return pyEngine;
}

/// <summary>
/// Creates a restricted Python execution engine
/// </summary>
/// <returns>The Python engine</returns>
public static ScriptEngine CreateRestrictedEngine()
{
Evidence evidence = new Evidence(); // an uninformative class name to limit accessible Assemblies
AppDomain domain = AppDomain.CreateDomain(GameMode.SaveGameDetails.Name, evidence);
ScriptEngine pyEngine = Python.CreateEngine(domain);
// Add Python standard lib to import search pathes
ICollection<string> searchPaths = pyEngine.GetSearchPaths();
searchPaths.Add(@".\Lib\"); // try not to use this location
searchPaths.Add(@".\Gamecraft_Data\Managed\Lib\");
if (!string.IsNullOrWhiteSpace(GameMode.SaveGameDetails.Folder))
{
// Add game's working directory, in case multiple files are used
searchPaths.Add(GameMode.SaveGameDetails.Folder);
}
pyEngine.SetSearchPaths(searchPaths);
return pyEngine;
}

/// <summary>
/// Creates the Python execution scope
/// </summary>
/// <returns>The Python scope</returns>
/// <param name="pyEngine">The Python engine</param>
public static ScriptScope CreateScope(ScriptEngine pyEngine)
{
ScriptScope pyScope = pyEngine.CreateScope();
// make "import GamecraftModdingAPI" work without the clr boilerplate
pyEngine.Execute("import clr\nclr.AddReference(\"GamecraftModdingAPI\")", pyScope);
return pyScope;
}
}
}

+ 1
- 0
GamecraftScripting/GamecraftScripting.csproj View File

@@ -2,6 +2,7 @@

<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<TargetFramework>net472</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Version>0.0.1.0</Version>
<Authors>Exmods</Authors>