Skip to content

Commit cc72be4

Browse files
authored
moved Py class into its own file (#1649)
1 parent 8d61215 commit cc72be4

File tree

2 files changed

+197
-187
lines changed

2 files changed

+197
-187
lines changed

src/runtime/Py.cs

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
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+
}

src/runtime/pythonengine.cs

Lines changed: 0 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
using System.Linq;
66
using System.Reflection;
77
using System.Runtime.InteropServices;
8-
using System.Runtime.Serialization;
9-
using System.Threading;
108

119
using Python.Runtime.Native;
1210

@@ -671,189 +669,4 @@ public enum RunFlagType : int
671669
File = 257, /* Py_file_input */
672670
Eval = 258
673671
}
674-
675-
public static class Py
676-
{
677-
public static GILState GIL()
678-
{
679-
if (!PythonEngine.IsInitialized)
680-
{
681-
PythonEngine.Initialize();
682-
}
683-
684-
return PythonEngine.DebugGIL ? new DebugGILState() : new GILState();
685-
}
686-
687-
public static PyModule CreateScope() => new();
688-
public static PyModule CreateScope(string name)
689-
=> new(name ?? throw new ArgumentNullException(nameof(name)));
690-
691-
692-
public class GILState : IDisposable
693-
{
694-
private readonly PyGILState state;
695-
private bool isDisposed;
696-
697-
internal GILState()
698-
{
699-
state = PythonEngine.AcquireLock();
700-
}
701-
702-
public virtual void Dispose()
703-
{
704-
if (this.isDisposed) return;
705-
706-
PythonEngine.ReleaseLock(state);
707-
GC.SuppressFinalize(this);
708-
this.isDisposed = true;
709-
}
710-
711-
~GILState()
712-
{
713-
throw new InvalidOperationException("GIL must always be released, and it must be released from the same thread that acquired it.");
714-
}
715-
}
716-
717-
public class DebugGILState : GILState
718-
{
719-
readonly Thread owner;
720-
internal DebugGILState() : base()
721-
{
722-
this.owner = Thread.CurrentThread;
723-
}
724-
public override void Dispose()
725-
{
726-
if (this.owner != Thread.CurrentThread)
727-
throw new InvalidOperationException("GIL must always be released from the same thread, that acquired it");
728-
729-
base.Dispose();
730-
}
731-
}
732-
733-
public class KeywordArguments : PyDict
734-
{
735-
public KeywordArguments() : base()
736-
{
737-
}
738-
739-
protected KeywordArguments(SerializationInfo info, StreamingContext context)
740-
: base(info, context) { }
741-
}
742-
743-
public static KeywordArguments kw(params object?[] kv)
744-
{
745-
var dict = new KeywordArguments();
746-
if (kv.Length % 2 != 0)
747-
{
748-
throw new ArgumentException("Must have an equal number of keys and values");
749-
}
750-
for (var i = 0; i < kv.Length; i += 2)
751-
{
752-
var key = kv[i] as string;
753-
if (key is null)
754-
throw new ArgumentException("Keys must be non-null strings");
755-
756-
BorrowedReference value;
757-
NewReference temp = default;
758-
if (kv[i + 1] is PyObject pyObj)
759-
{
760-
value = pyObj;
761-
}
762-
else
763-
{
764-
temp = Converter.ToPythonDetectType(kv[i + 1]);
765-
value = temp.Borrow();
766-
}
767-
using (temp)
768-
{
769-
if (Runtime.PyDict_SetItemString(dict, key, value) != 0)
770-
{
771-
throw new ArgumentException(
772-
string.Format("Cannot add key '{0}' to dictionary.", key),
773-
innerException: PythonException.FetchCurrent());
774-
}
775-
}
776-
}
777-
return dict;
778-
}
779-
780-
/// <summary>
781-
/// Given a module or package name, import the module and return the resulting object.
782-
/// </summary>
783-
/// <param name="name">Fully-qualified module or package name</param>
784-
public static PyObject Import(string name) => PyModule.Import(name);
785-
786-
public static void SetArgv()
787-
{
788-
IEnumerable<string> args;
789-
try
790-
{
791-
args = Environment.GetCommandLineArgs();
792-
}
793-
catch (NotSupportedException)
794-
{
795-
args = Enumerable.Empty<string>();
796-
}
797-
798-
SetArgv(
799-
new[] { "" }.Concat(
800-
Environment.GetCommandLineArgs().Skip(1)
801-
)
802-
);
803-
}
804-
805-
public static void SetArgv(params string[] argv)
806-
{
807-
SetArgv(argv as IEnumerable<string>);
808-
}
809-
810-
public static void SetArgv(IEnumerable<string> argv)
811-
{
812-
if (argv is null) throw new ArgumentNullException(nameof(argv));
813-
814-
using (GIL())
815-
{
816-
string[] arr = argv.ToArray();
817-
Runtime.PySys_SetArgvEx(arr.Length, arr, 0);
818-
Runtime.CheckExceptionOccurred();
819-
}
820-
}
821-
822-
public static void With(PyObject obj, Action<dynamic> Body)
823-
{
824-
if (obj is null) throw new ArgumentNullException(nameof(obj));
825-
if (Body is null) throw new ArgumentNullException(nameof(Body));
826-
827-
// Behavior described here:
828-
// https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
829-
830-
Exception? ex = null;
831-
PythonException? pyError = null;
832-
833-
try
834-
{
835-
PyObject enterResult = obj.InvokeMethod("__enter__");
836-
837-
Body(enterResult);
838-
}
839-
catch (PythonException e)
840-
{
841-
ex = pyError = e;
842-
}
843-
catch (Exception e)
844-
{
845-
ex = e;
846-
Exceptions.SetError(e);
847-
pyError = PythonException.FetchCurrentRaw();
848-
}
849-
850-
PyObject type = pyError?.Type ?? PyObject.None;
851-
PyObject val = pyError?.Value ?? PyObject.None;
852-
PyObject traceBack = pyError?.Traceback ?? PyObject.None;
853-
854-
var exitResult = obj.InvokeMethod("__exit__", type, val, traceBack);
855-
856-
if (ex != null && !exitResult.IsTrue()) throw ex;
857-
}
858-
}
859672
}

0 commit comments

Comments
 (0)