From dda5267696c6d19cdf55bbbd0cbd8c1d37af1723 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Fri, 1 Oct 2021 10:21:42 -0700 Subject: [PATCH] safer GetAttr(name, default) fixes https://github.com/pythonnet/pythonnet/issues/1036 --- CHANGELOG.md | 1 + src/embed_tests/TestPyObject.cs | 21 ++++++++++++++ src/runtime/pyobject.cs | 51 ++++++++++++++++++++++++++------- 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cb3fc14f..876bff07d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ See [Mixins/collections.py](src/runtime/Mixins/collections.py). - BREAKING: When trying to convert Python `int` to `System.Object`, result will be of type `PyInt` instead of `System.Int32` due to possible loss of information. Python `float` will continue to be converted to `System.Double`. +- BREAKING: `PyObject.GetAttr(name, default)` now only ignores `AttributeError` (previously ignored all exceptions). - BREAKING: `PyObject` no longer implements `IEnumerable`. Instead, `PyIterable` does that. diff --git a/src/embed_tests/TestPyObject.cs b/src/embed_tests/TestPyObject.cs index f7f07e6a4..238f53530 100644 --- a/src/embed_tests/TestPyObject.cs +++ b/src/embed_tests/TestPyObject.cs @@ -79,5 +79,26 @@ public void UnaryMinus_ThrowsOnBadType() var error = Assert.Throws(() => list = -list); Assert.AreEqual("TypeError", error.Type.Name); } + + [Test] + [Obsolete] + public void GetAttrDefault_IgnoresAttributeErrorOnly() + { + var ob = new PyObjectTestMethods().ToPython(); + using var fallback = new PyList(); + var attrErrResult = ob.GetAttr(nameof(PyObjectTestMethods.RaisesAttributeError), fallback); + Assert.IsTrue(PythonReferenceComparer.Instance.Equals(fallback, attrErrResult)); + + var typeErrResult = Assert.Throws( + () => ob.GetAttr(nameof(PyObjectTestMethods.RaisesTypeError), fallback) + ); + Assert.AreEqual(Exceptions.TypeError, typeErrResult.Type.Handle); + } + } + + public class PyObjectTestMethods + { + public string RaisesAttributeError => throw new PythonException(new PyType(new BorrowedReference(Exceptions.AttributeError)), value: null, traceback: null); + public string RaisesTypeError => throw new PythonException(new PyType(new BorrowedReference(Exceptions.TypeError)), value: null, traceback: null); } } diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs index 05c482454..7a57f6f87 100644 --- a/src/runtime/pyobject.cs +++ b/src/runtime/pyobject.cs @@ -309,12 +309,20 @@ public PyObject GetAttr(string name) /// - /// GetAttr Method. Returns fallback value if getting attribute fails for any reason. + /// Returns the named attribute of the Python object, or the given + /// default object if the attribute access throws AttributeError. /// /// - /// Returns the named attribute of the Python object, or the given - /// default object if the attribute access fails. + /// This method ignores any AttrubiteError(s), even ones + /// not raised due to missing requested attribute. + /// + /// For example, if attribute getter calls other Python code, and + /// that code happens to cause AttributeError elsewhere, it will be ignored + /// and value will be returned instead. /// + /// Name of the attribute. + /// The object to return on AttributeError. + [Obsolete("See remarks")] public PyObject GetAttr(string name, PyObject _default) { if (name == null) throw new ArgumentNullException(nameof(name)); @@ -322,8 +330,15 @@ public PyObject GetAttr(string name, PyObject _default) IntPtr op = Runtime.PyObject_GetAttrString(obj, name); if (op == IntPtr.Zero) { - Runtime.PyErr_Clear(); - return _default; + if (Exceptions.ExceptionMatches(Exceptions.AttributeError)) + { + Runtime.PyErr_Clear(); + return _default; + } + else + { + throw PythonException.ThrowLastAsClrException(); + } } return new PyObject(op); } @@ -351,13 +366,20 @@ public PyObject GetAttr(PyObject name) /// - /// GetAttr Method + /// Returns the named attribute of the Python object, or the given + /// default object if the attribute access throws AttributeError. /// /// - /// Returns the named attribute of the Python object, or the given - /// default object if the attribute access fails. The name argument - /// is a PyObject wrapping a Python string or unicode object. + /// This method ignores any AttrubiteError(s), even ones + /// not raised due to missing requested attribute. + /// + /// For example, if attribute getter calls other Python code, and + /// that code happens to cause AttributeError elsewhere, it will be ignored + /// and value will be returned instead. /// + /// Name of the attribute. Must be of Python type 'str'. + /// The object to return on AttributeError. + [Obsolete("See remarks")] public PyObject GetAttr(PyObject name, PyObject _default) { if (name == null) throw new ArgumentNullException(nameof(name)); @@ -365,8 +387,15 @@ public PyObject GetAttr(PyObject name, PyObject _default) IntPtr op = Runtime.PyObject_GetAttr(obj, name.obj); if (op == IntPtr.Zero) { - Runtime.PyErr_Clear(); - return _default; + if (Exceptions.ExceptionMatches(Exceptions.AttributeError)) + { + Runtime.PyErr_Clear(); + return _default; + } + else + { + throw PythonException.ThrowLastAsClrException(); + } } return new PyObject(op); } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy