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+}