using System;
using System.Linq;
using System.Collections.Generic;
using System.Dynamic;
using System.Runtime.Serialization;
namespace Python.Runtime
{
[Serializable]
public class PyModule : PyObject
{
internal BorrowedReference variables => VarsRef;
internal BorrowedReference VarsRef
{
get
{
var vars = Runtime.PyModule_GetDict(Reference);
PythonException.ThrowIfIsNull(vars);
return vars;
}
}
public PyModule(string name = "") : this(Create(name))
{
InitializeBuiltins();
}
static StolenReference Create(string name)
{
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}
return Runtime.PyModule_New(name).StealOrThrow();
}
internal PyModule(in StolenReference reference) : base(reference)
{
if (!IsModule(Reference))
{
throw new ArgumentException("object is not a module");
}
}
protected PyModule(SerializationInfo info, StreamingContext context)
: base(info, context) { }
private void InitializeBuiltins()
{
int res = Runtime.PyDict_SetItem(
VarsRef, PyIdentifier.__builtins__,
Runtime.PyEval_GetBuiltins()
);
PythonException.ThrowIfIsNotZero(res);
}
internal PyModule(BorrowedReference reference) : this(new NewReference(reference).Steal())
{
}
///
/// Given a module or package name, import the module and return the resulting object.
///
/// Fully-qualified module or package name
public static PyObject Import(string name)
{
if (name is null) throw new ArgumentNullException(nameof(name));
NewReference op = Runtime.PyImport_ImportModule(name);
return IsModule(op.BorrowOrThrow()) ? new PyModule(op.Steal()) : op.MoveToPyObject();
}
///
/// Reloads the module, and returns the updated object
///
public PyModule Reload()
{
NewReference op = Runtime.PyImport_ReloadModule(this.Reference);
return new PyModule(op.StealOrThrow());
}
public static PyModule FromString(string name, string code)
{
return FromString(name, code, "");
}
public static PyModule FromString(string name, string code, string file)
{
// Force valid value
if (string.IsNullOrWhiteSpace(file))
{
file = "none";
}
using NewReference c = Runtime.Py_CompileString(code, file, (int)RunFlagType.File);
NewReference m = Runtime.PyImport_ExecCodeModule(name, c.BorrowOrThrow());
return new PyModule(m.StealOrThrow());
}
public PyModule SetBuiltins(PyDict builtins)
{
if (builtins == null || builtins.IsNone())
{
throw new ArgumentNullException(nameof(builtins));
}
BorrowedReference globals = Runtime.PyModule_GetDict(this.Reference);
PythonException.ThrowIfIsNull(globals);
int rc = Runtime.PyDict_SetItemString(globals, "__builtins__", builtins.Reference);
PythonException.ThrowIfIsNotZero(rc);
return this;
}
public static PyDict SysModules
{
get
{
BorrowedReference sysModulesRef = Runtime.PyImport_GetModuleDict();
PythonException.ThrowIfIsNull(sysModulesRef);
return new PyDict(sysModulesRef);
}
}
internal static bool IsModule(BorrowedReference reference)
{
if (reference == null) return false;
BorrowedReference type = Runtime.PyObject_TYPE(reference);
return Runtime.PyType_IsSubtype(type, Runtime.PyModuleType);
}
///
/// Returns the variables dict of the module.
///
public PyDict Variables() => new(VarsRef);
///
/// Create a scope, and import all from this scope
///
public PyModule NewScope()
{
var scope = new PyModule();
scope.ImportAll(this);
return scope;
}
///
/// Import module by its name.
///
public PyObject Import(string name, string? asname = null)
{
Check();
asname ??= name;
var module = PyModule.Import(name);
Import(module, asname);
return module;
}
///
/// Import module as a variable of given name.
///
public void Import(PyModule module, string asname)
{
if (module is null) throw new ArgumentNullException(nameof(module));
if (asname is null) throw new ArgumentNullException(nameof(asname));
this.SetPyValue(asname, module);
}
///
/// The 'import .. as ..' statement in Python.
/// Import a module as a variable.
///
public void Import(PyObject module, string? asname = null)
{
if (module is null) throw new ArgumentNullException(nameof(module));
asname ??= module.GetAttr("__name__").As();
if (asname is null) throw new ArgumentException("Module has no name");
Set(asname, module);
}
///
/// Import all variables of the module into this module.
///
public void ImportAll(PyModule module)
{
if (module is null) throw new ArgumentNullException(nameof(module));
int result = Runtime.PyDict_Update(VarsRef, module.VarsRef);
if (result < 0)
{
throw PythonException.ThrowLastAsClrException();
}
}
///
/// Import all variables of the module into this module.
///
public void ImportAll(PyObject module)
{
if (module is null) throw new ArgumentNullException(nameof(module));
if (!IsModule(module.Reference))
{
throw new ArgumentException("object is not a module", paramName: nameof(module));
}
var module_dict = Runtime.PyModule_GetDict(module.Reference);
int result = Runtime.PyDict_Update(VarsRef, module_dict);
if (result < 0)
{
throw PythonException.ThrowLastAsClrException();
}
}
///
/// Import all variables in the dictionary into this module.
///
public void ImportAll(PyDict dict)
{
if (dict is null) throw new ArgumentNullException(nameof(dict));
int result = Runtime.PyDict_Update(VarsRef, dict.Reference);
if (result < 0)
{
throw PythonException.ThrowLastAsClrException();
}
}
///
/// Execute method
///
///
/// Execute a Python ast and return the result as a PyObject.
/// The ast can be either an expression or stmts.
///
public PyObject Execute(PyObject script, PyDict? locals = null)
{
if (script is null) throw new ArgumentNullException(nameof(script));
Check();
BorrowedReference _locals = locals == null ? variables : locals.obj;
using var ptr = Runtime.PyEval_EvalCode(script, variables, _locals);
PythonException.ThrowIfIsNull(ptr);
return ptr.MoveToPyObject();
}
///
/// Execute a Python ast and return the result as a ,
/// and convert the result to a Managed Object of given type.
/// The ast can be either an expression or stmts.
///
public T? Execute(PyObject script, PyDict? locals = null)
{
Check();
PyObject pyObj = Execute(script, locals);
var obj = pyObj.As();
return obj;
}
///
/// Evaluate a Python expression and return the result as a .
///
public PyObject Eval(string code, PyDict? locals = null)
{
if (code is null) throw new ArgumentNullException(nameof(code));
Check();
BorrowedReference _locals = locals == null ? VarsRef : locals.Reference;
NewReference reference = Runtime.PyRun_String(
code, RunFlagType.Eval, VarsRef, _locals
);
PythonException.ThrowIfIsNull(reference);
return reference.MoveToPyObject();
}
///
/// Evaluate a Python expression
///
///
/// Evaluate a Python expression
/// and convert the result to a Managed Object of given type.
///
public T? Eval(string code, PyDict? locals = null)
{
Check();
PyObject pyObj = Eval(code, locals);
var obj = pyObj.As();
return obj;
}
///
/// Exec Method
///
///
/// Exec a Python script and save its local variables in the current local variable dict.
///
public PyModule Exec(string code, PyDict? locals = null)
{
Check();
BorrowedReference _locals = locals == null ? VarsRef : locals.Reference;
Exec(code, VarsRef, _locals);
return this;
}
private void Exec(string code, BorrowedReference _globals, BorrowedReference _locals)
{
using NewReference reference = Runtime.PyRun_String(
code, RunFlagType.File, _globals, _locals
);
PythonException.ThrowIfIsNull(reference);
}
///
/// Set Variable Method
///
///
/// Add a new variable to the variables dict if it not exist
/// or update its value if the variable exists.
///
public PyModule Set(string name, object? value)
{
if (name is null) throw new ArgumentNullException(nameof(name));
using var _value = Converter.ToPythonDetectType(value);
SetPyValue(name, _value.Borrow());
return this;
}
private void SetPyValue(string name, BorrowedReference value)
{
Check();
using var pyKey = new PyString(name);
int r = Runtime.PyObject_SetItem(variables, pyKey.obj, value);
if (r < 0)
{
throw PythonException.ThrowLastAsClrException();
}
}
///
/// Remove Method
///
///
/// Remove a variable from the variables dict.
///
public PyModule Remove(string name)
{
if (name is null) throw new ArgumentNullException(nameof(name));
Check();
using (var pyKey = new PyString(name))
{
int r = Runtime.PyObject_DelItem(variables, pyKey.obj);
if (r < 0)
{
throw PythonException.ThrowLastAsClrException();
}
}
return this;
}
///
/// Returns true if the variable exists in the module.
///
public bool Contains(string name)
{
if (name is null) throw new ArgumentNullException(nameof(name));
Check();
using var pyKey = new PyString(name);
return Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0;
}
///
/// Returns the value of the variable with the given name.
///
///
/// Thrown when variable with the given name does not exist.
///
public PyObject Get(string name)
{
if (name is null) throw new ArgumentNullException(nameof(name));
var state = TryGet(name, out var value);
if (!state)
{
throw new KeyNotFoundException($"The module has no attribute '{name}'");
}
return value!;
}
///
/// TryGet Method
///
///
/// Returns the value of the variable, local variable first.
/// If the variable does not exist, return null.
///
public bool TryGet(string name, out PyObject? value)
{
if (name is null) throw new ArgumentNullException(nameof(name));
Check();
using var pyKey = new PyString(name);
if (Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0)
{
using var op = Runtime.PyObject_GetItem(variables, pyKey.obj);
value = new PyObject(op.StealOrThrow());
return true;
}
else
{
value = null;
return false;
}
}
///
/// Get Method
///
///
/// Obtain the value of the variable of given name,
/// and convert the result to a Managed Object of given type.
/// If the variable does not exist, throw an Exception.
///
public T Get(string name)
{
if (name is null) throw new ArgumentNullException(nameof(name));
Check();
PyObject pyObj = Get(name);
return pyObj.As()!;
}
///
/// TryGet Method
///
///
/// Obtain the value of the variable of given name,
/// and convert the result to a Managed Object of given type.
/// If the variable does not exist, return false.
///
public bool TryGet(string name, out T? value)
{
Check();
var result = TryGet(name, out var pyObj);
if (!result)
{
value = default;
return false;
}
value = pyObj!.As();
return true;
}
public override bool TryGetMember(GetMemberBinder binder, out object? result)
{
result = CheckNone(this.Get(binder.Name));
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object? value)
{
this.Set(binder.Name, value);
return true;
}
private void Check()
{
if (this.rawPtr == IntPtr.Zero)
{
throw new ObjectDisposedException(nameof(PyModule));
}
}
}
}