Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 00c19d5

Browse files
authored
Merge branch 'master' into domain-reload-test-cases-fixes
2 parents 5c14aad + 1f40564 commit 00c19d5

File tree

15 files changed

+191
-91
lines changed

15 files changed

+191
-91
lines changed

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
- Sean Freitag ([@cowboygneox](https://github.com/cowboygneox))
6161
- Serge Weinstock ([@sweinst](https://github.com/sweinst))
6262
- Simon Mourier ([@smourier](https://github.com/smourier))
63+
- Tom Minka ([@tminka](https://github.com/tminka))
6364
- Viktoria Kovescses ([@vkovec](https://github.com/vkovec))
6465
- Ville M. Vainio ([@vivainio](https://github.com/vivainio))
6566
- Virgil Dupras ([@hsoft](https://github.com/hsoft))

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ details about the cause of the failure
2626
- BREAKING: Parameters marked with `ParameterAttributes.Out` are no longer returned in addition
2727
to the regular method return value (unless they are passed with `ref` or `out` keyword).
2828
- BREAKING: Drop support for the long-deprecated CLR.* prefix.
29+
- `PyObject` now implements `IEnumerable<PyObject>` in addition to `IEnumerable`
2930

3031
### Fixed
3132

@@ -39,7 +40,10 @@ details about the cause of the failure
3940
- Indexers can now be used with interface objects
4041
- Fixed a bug where indexers could not be used if they were inherited
4142
- Made it possible to use `__len__` also on `ICollection<>` interface objects
43+
- Fixed issue when calling PythonException.Format where another exception would be raise for unnormalized exceptions
4244
- Made it possible to call `ToString`, `GetHashCode`, and `GetType` on inteface objects
45+
- Fixed objects returned by enumerating `PyObject` being disposed too soon
46+
- Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException
4347

4448
### Removed
4549

pythonnet/mono/.gitkeep

Whitespace-only changes.

pythonnet/netfx/.gitkeep

Whitespace-only changes.

pythonnet/runtime/.gitkeep

Whitespace-only changes.

src/embed_tests/TestPyIter.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Linq;
2+
using System.Text;
3+
4+
using NUnit.Framework;
5+
6+
using Python.Runtime;
7+
8+
namespace Python.EmbeddingTest
9+
{
10+
class TestPyIter
11+
{
12+
[OneTimeSetUp]
13+
public void SetUp()
14+
{
15+
PythonEngine.Initialize();
16+
}
17+
18+
[OneTimeTearDown]
19+
public void Dispose()
20+
{
21+
PythonEngine.Shutdown();
22+
}
23+
24+
[Test]
25+
public void KeepOldObjects()
26+
{
27+
using (Py.GIL())
28+
using (var testString = new PyString("hello world! !$%&/()=?"))
29+
{
30+
PyObject[] chars = testString.ToArray();
31+
Assert.IsTrue(chars.Length > 1);
32+
string reconstructed = string.Concat(chars.Select(c => c.As<string>()));
33+
Assert.AreEqual(testString.As<string>(), reconstructed);
34+
}
35+
}
36+
}
37+
}

src/embed_tests/TestPythonEngineProperties.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,37 @@ public void SetProgramName()
181181
public void SetPythonPath()
182182
{
183183
PythonEngine.Initialize();
184-
string path = PythonEngine.PythonPath;
184+
185+
const string moduleName = "pytest";
186+
bool importShouldSucceed;
187+
try
188+
{
189+
Py.Import(moduleName);
190+
importShouldSucceed = true;
191+
}
192+
catch
193+
{
194+
importShouldSucceed = false;
195+
}
196+
197+
string[] paths = Py.Import("sys").GetAttr("path").As<string[]>();
198+
string path = string.Join(System.IO.Path.PathSeparator.ToString(), paths);
199+
200+
// path should not be set to PythonEngine.PythonPath here.
201+
// PythonEngine.PythonPath gets the default module search path, not the full search path.
202+
// The list sys.path is initialized with this value on interpreter startup;
203+
// it can be (and usually is) modified later to change the search path for loading modules.
204+
// See https://docs.python.org/3/c-api/init.html#c.Py_GetPath
205+
// After PythonPath is set, then PythonEngine.PythonPath will correctly return the full search path.
206+
185207
PythonEngine.Shutdown();
186208

187209
PythonEngine.PythonPath = path;
188210
PythonEngine.Initialize();
189211

190212
Assert.AreEqual(path, PythonEngine.PythonPath);
213+
if (importShouldSucceed) Py.Import(moduleName);
214+
191215
PythonEngine.Shutdown();
192216
}
193217
}

src/embed_tests/TestPythonException.cs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,54 @@ public void TestPythonExceptionFormatNoTraceback()
8686
}
8787
catch (PythonException ex)
8888
{
89-
// ImportError/ModuleNotFoundError do not have a traceback when not running in a script
89+
// ImportError/ModuleNotFoundError do not have a traceback when not running in a script
9090
Assert.AreEqual(ex.StackTrace, ex.Format());
9191
}
9292
}
93+
94+
[Test]
95+
public void TestPythonExceptionFormatNormalized()
96+
{
97+
try
98+
{
99+
PythonEngine.Exec("a=b\n");
100+
}
101+
catch (PythonException ex)
102+
{
103+
Assert.AreEqual("Traceback (most recent call last):\n File \"<string>\", line 1, in <module>\nNameError: name 'b' is not defined\n", ex.Format());
104+
}
105+
}
106+
107+
[Test]
108+
public void TestPythonException_PyErr_NormalizeException()
109+
{
110+
using (var scope = Py.CreateScope())
111+
{
112+
scope.Exec(@"
113+
class TestException(NameError):
114+
def __init__(self, val):
115+
super().__init__(val)
116+
x = int(val)");
117+
Assert.IsTrue(scope.TryGet("TestException", out PyObject type));
118+
119+
PyObject str = "dummy string".ToPython();
120+
IntPtr typePtr = type.Handle;
121+
IntPtr strPtr = str.Handle;
122+
IntPtr tbPtr = Runtime.Runtime.None.Handle;
123+
Runtime.Runtime.XIncref(typePtr);
124+
Runtime.Runtime.XIncref(strPtr);
125+
Runtime.Runtime.XIncref(tbPtr);
126+
Runtime.Runtime.PyErr_NormalizeException(ref typePtr, ref strPtr, ref tbPtr);
127+
128+
using (PyObject typeObj = new PyObject(typePtr), strObj = new PyObject(strPtr), tbObj = new PyObject(tbPtr))
129+
{
130+
// the type returned from PyErr_NormalizeException should not be the same type since a new
131+
// exception was raised by initializing the exception
132+
Assert.AreNotEqual(type.Handle, typePtr);
133+
// the message should now be the string from the throw exception during normalization
134+
Assert.AreEqual("invalid literal for int() with base 10: 'dummy string'", strObj.ToString());
135+
}
136+
}
137+
}
93138
}
94139
}

src/runtime/classbase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public virtual IntPtr type_subscript(IntPtr idx)
6262
return c.pyHandle;
6363
}
6464

65-
return Exceptions.RaiseTypeError("no type matches params");
65+
return Exceptions.RaiseTypeError($"{type.Namespace}.{type.Name} does not accept {types.Length} generic parameters");
6666
}
6767

6868
/// <summary>

src/runtime/genericutil.cs

Lines changed: 40 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@ namespace Python.Runtime
99
/// This class is responsible for efficiently maintaining the bits
1010
/// of information we need to support aliases with 'nice names'.
1111
/// </summary>
12-
internal class GenericUtil
12+
internal static class GenericUtil
1313
{
14+
/// <summary>
15+
/// Maps namespace -> generic base name -> list of generic type names
16+
/// </summary>
1417
private static Dictionary<string, Dictionary<string, List<string>>> mapping;
1518

16-
private GenericUtil()
17-
{
18-
}
19-
2019
public static void Reset()
2120
{
2221
mapping = new Dictionary<string, Dictionary<string, List<string>>>();
@@ -25,29 +24,23 @@ public static void Reset()
2524
/// <summary>
2625
/// Register a generic type that appears in a given namespace.
2726
/// </summary>
27+
/// <param name="t">A generic type definition (<c>t.IsGenericTypeDefinition</c> must be true)</param>
2828
internal static void Register(Type t)
2929
{
3030
if (null == t.Namespace || null == t.Name)
3131
{
3232
return;
3333
}
3434

35-
Dictionary<string, List<string>> nsmap = null;
36-
mapping.TryGetValue(t.Namespace, out nsmap);
37-
if (nsmap == null)
35+
Dictionary<string, List<string>> nsmap;
36+
if (!mapping.TryGetValue(t.Namespace, out nsmap))
3837
{
3938
nsmap = new Dictionary<string, List<string>>();
4039
mapping[t.Namespace] = nsmap;
4140
}
42-
string basename = t.Name;
43-
int tick = basename.IndexOf("`");
44-
if (tick > -1)
45-
{
46-
basename = basename.Substring(0, tick);
47-
}
48-
List<string> gnames = null;
49-
nsmap.TryGetValue(basename, out gnames);
50-
if (gnames == null)
41+
string basename = GetBasename(t.Name);
42+
List<string> gnames;
43+
if (!nsmap.TryGetValue(basename, out gnames))
5144
{
5245
gnames = new List<string>();
5346
nsmap[basename] = gnames;
@@ -60,9 +53,8 @@ internal static void Register(Type t)
6053
/// </summary>
6154
public static List<string> GetGenericBaseNames(string ns)
6255
{
63-
Dictionary<string, List<string>> nsmap = null;
64-
mapping.TryGetValue(ns, out nsmap);
65-
if (nsmap == null)
56+
Dictionary<string, List<string>> nsmap;
57+
if (!mapping.TryGetValue(ns, out nsmap))
6658
{
6759
return null;
6860
}
@@ -75,84 +67,73 @@ public static List<string> GetGenericBaseNames(string ns)
7567
}
7668

7769
/// <summary>
78-
/// xxx
70+
/// Finds a generic type with the given number of generic parameters and the same name and namespace as <paramref name="t"/>.
7971
/// </summary>
8072
public static Type GenericForType(Type t, int paramCount)
8173
{
8274
return GenericByName(t.Namespace, t.Name, paramCount);
8375
}
8476

85-
public static Type GenericByName(string ns, string name, int paramCount)
86-
{
87-
foreach (Type t in GenericsByName(ns, name))
88-
{
89-
if (t.GetGenericArguments().Length == paramCount)
90-
{
91-
return t;
92-
}
93-
}
94-
return null;
95-
}
96-
97-
public static List<Type> GenericsForType(Type t)
98-
{
99-
return GenericsByName(t.Namespace, t.Name);
100-
}
101-
102-
public static List<Type> GenericsByName(string ns, string basename)
77+
/// <summary>
78+
/// Finds a generic type in the given namespace with the given name and number of generic parameters.
79+
/// </summary>
80+
public static Type GenericByName(string ns, string basename, int paramCount)
10381
{
104-
Dictionary<string, List<string>> nsmap = null;
105-
mapping.TryGetValue(ns, out nsmap);
106-
if (nsmap == null)
82+
Dictionary<string, List<string>> nsmap;
83+
if (!mapping.TryGetValue(ns, out nsmap))
10784
{
10885
return null;
10986
}
11087

111-
int tick = basename.IndexOf("`");
112-
if (tick > -1)
113-
{
114-
basename = basename.Substring(0, tick);
115-
}
116-
117-
List<string> names = null;
118-
nsmap.TryGetValue(basename, out names);
119-
if (names == null)
88+
List<string> names;
89+
if (!nsmap.TryGetValue(GetBasename(basename), out names))
12090
{
12191
return null;
12292
}
12393

124-
var result = new List<Type>();
12594
foreach (string name in names)
12695
{
12796
string qname = ns + "." + name;
12897
Type o = AssemblyManager.LookupTypes(qname).FirstOrDefault();
129-
if (o != null)
98+
if (o != null && o.GetGenericArguments().Length == paramCount)
13099
{
131-
result.Add(o);
100+
return o;
132101
}
133102
}
134103

135-
return result;
104+
return null;
136105
}
137106

138107
/// <summary>
139108
/// xxx
140109
/// </summary>
141110
public static string GenericNameForBaseName(string ns, string name)
142111
{
143-
Dictionary<string, List<string>> nsmap = null;
144-
mapping.TryGetValue(ns, out nsmap);
145-
if (nsmap == null)
112+
Dictionary<string, List<string>> nsmap;
113+
if (!mapping.TryGetValue(ns, out nsmap))
146114
{
147115
return null;
148116
}
149-
List<string> gnames = null;
117+
List<string> gnames;
150118
nsmap.TryGetValue(name, out gnames);
151119
if (gnames?.Count > 0)
152120
{
153121
return gnames[0];
154122
}
155123
return null;
156124
}
125+
126+
private static string GetBasename(string name)
127+
{
128+
int tick = name.IndexOf("`");
129+
if (tick > -1)
130+
{
131+
return name.Substring(0, tick);
132+
}
133+
else
134+
{
135+
return name;
136+
}
137+
}
157138
}
158139
}

0 commit comments

Comments
 (0)