moved Py class into its own file (#1649) · pythonnet/pythonnet@cc72be4

1+

namespace Python.Runtime;

2+3+

using System;

4+

using System.Collections.Generic;

5+

using System.Linq;

6+

using System.Runtime.Serialization;

7+

using System.Threading;

8+9+

using Python.Runtime.Native;

10+11+

public static class Py

12+

{

13+

public static GILState GIL()

14+

{

15+

if (!PythonEngine.IsInitialized)

16+

{

17+

PythonEngine.Initialize();

18+

}

19+20+

return PythonEngine.DebugGIL ? new DebugGILState() : new GILState();

21+

}

22+23+

public static PyModule CreateScope() => new();

24+

public static PyModule CreateScope(string name)

25+

=> new(name ?? throw new ArgumentNullException(nameof(name)));

26+27+28+

public class GILState : IDisposable

29+

{

30+

private readonly PyGILState state;

31+

private bool isDisposed;

32+33+

internal GILState()

34+

{

35+

state = PythonEngine.AcquireLock();

36+

}

37+38+

public virtual void Dispose()

39+

{

40+

if (this.isDisposed) return;

41+42+

PythonEngine.ReleaseLock(state);

43+

GC.SuppressFinalize(this);

44+

this.isDisposed = true;

45+

}

46+47+

~GILState()

48+

{

49+

throw new InvalidOperationException("GIL must always be released, and it must be released from the same thread that acquired it.");

50+

}

51+

}

52+53+

public class DebugGILState : GILState

54+

{

55+

readonly Thread owner;

56+

internal DebugGILState() : base()

57+

{

58+

this.owner = Thread.CurrentThread;

59+

}

60+

public override void Dispose()

61+

{

62+

if (this.owner != Thread.CurrentThread)

63+

throw new InvalidOperationException("GIL must always be released from the same thread, that acquired it");

64+65+

base.Dispose();

66+

}

67+

}

68+69+

public class KeywordArguments : PyDict

70+

{

71+

public KeywordArguments() : base()

72+

{

73+

}

74+75+

protected KeywordArguments(SerializationInfo info, StreamingContext context)

76+

: base(info, context) { }

77+

}

78+79+

public static KeywordArguments kw(params object?[] kv)

80+

{

81+

var dict = new KeywordArguments();

82+

if (kv.Length % 2 != 0)

83+

{

84+

throw new ArgumentException("Must have an equal number of keys and values");

85+

}

86+

for (var i = 0; i < kv.Length; i += 2)

87+

{

88+

var key = kv[i] as string;

89+

if (key is null)

90+

throw new ArgumentException("Keys must be non-null strings");

91+92+

BorrowedReference value;

93+

NewReference temp = default;

94+

if (kv[i + 1] is PyObject pyObj)

95+

{

96+

value = pyObj;

97+

}

98+

else

99+

{

100+

temp = Converter.ToPythonDetectType(kv[i + 1]);

101+

value = temp.Borrow();

102+

}

103+

using (temp)

104+

{

105+

if (Runtime.PyDict_SetItemString(dict, key, value) != 0)

106+

{

107+

throw new ArgumentException(

108+

string.Format("Cannot add key '{0}' to dictionary.", key),

109+

innerException: PythonException.FetchCurrent());

110+

}

111+

}

112+

}

113+

return dict;

114+

}

115+116+

/// <summary>

117+

/// Given a module or package name, import the module and return the resulting object.

118+

/// </summary>

119+

/// <param name="name">Fully-qualified module or package name</param>

120+

public static PyObject Import(string name) => PyModule.Import(name);

121+122+

public static void SetArgv()

123+

{

124+

IEnumerable<string> args;

125+

try

126+

{

127+

args = Environment.GetCommandLineArgs();

128+

}

129+

catch (NotSupportedException)

130+

{

131+

args = Enumerable.Empty<string>();

132+

}

133+134+

SetArgv(

135+

new[] { "" }.Concat(

136+

Environment.GetCommandLineArgs().Skip(1)

137+

)

138+

);

139+

}

140+141+

public static void SetArgv(params string[] argv)

142+

{

143+

SetArgv(argv as IEnumerable<string>);

144+

}

145+146+

public static void SetArgv(IEnumerable<string> argv)

147+

{

148+

if (argv is null) throw new ArgumentNullException(nameof(argv));

149+150+

using (GIL())

151+

{

152+

string[] arr = argv.ToArray();

153+

Runtime.PySys_SetArgvEx(arr.Length, arr, 0);

154+

Runtime.CheckExceptionOccurred();

155+

}

156+

}

157+158+

public static void With(PyObject obj, Action<PyObject> Body)

159+

{

160+

if (obj is null) throw new ArgumentNullException(nameof(obj));

161+

if (Body is null) throw new ArgumentNullException(nameof(Body));

162+163+

// Behavior described here:

164+

// https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers

165+166+

Exception? ex = null;

167+

PythonException? pyError = null;

168+169+

try

170+

{

171+

PyObject enterResult = obj.InvokeMethod("__enter__");

172+173+

Body(enterResult);

174+

}

175+

catch (PythonException e)

176+

{

177+

ex = pyError = e;

178+

}

179+

catch (Exception e)

180+

{

181+

ex = e;

182+

Exceptions.SetError(e);

183+

pyError = PythonException.FetchCurrentRaw();

184+

}

185+186+

PyObject type = pyError?.Type ?? PyObject.None;

187+

PyObject val = pyError?.Value ?? PyObject.None;

188+

PyObject traceBack = pyError?.Traceback ?? PyObject.None;

189+190+

var exitResult = obj.InvokeMethod("__exit__", type, val, traceBack);

191+192+

if (ex != null && !exitResult.IsTrue()) throw ex;

193+

}

194+195+

public static void With(PyObject obj, Action<dynamic> Body)

196+

=> With(obj, (PyObject context) => Body(context));

197+

}